From 98242cc7f14c8a6423ca44c600ad62431e283c19 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 8 Feb 2024 18:01:03 +0100 Subject: [PATCH 01/31] update cmake --- .github/workflows/CI.yml | 2 +- CMakeLists.txt | 27 ++++++++++++++---------- Cmake/Setup.cmake | 33 ++++++++++++++++++++---------- Include/SevenBit/Conf/CmakeDef.hpp | 4 ++-- SingleHeader/CMakeLists.txt | 2 +- Source/CMakeLists.txt | 26 ++++++++++++++++------- Tests/CMakeLists.txt | 12 ++++++++++- 7 files changed, 72 insertions(+), 34 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f33a56e..a6bb334 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,7 +22,7 @@ jobs: runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Conan Packages id: conan diff --git a/CMakeLists.txt b/CMakeLists.txt index 490a760..277ecfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,22 +1,27 @@ -cmake_minimum_required(VERSION 3.15.0) +cmake_minimum_required(VERSION 3.5.0) + +set(_7BIT_CONF_LIBRARY 7bitConf) set(_7BIT_CONF_VERSION_MAJOR 1) set(_7BIT_CONF_VERSION_MINOR 1) -set(_7BIT_CONF_VERSION_PATCH 0) +set(_7BIT_CONF_VERSION_PATCH 1) set(_7BIT_CONF_VERSION ${_7BIT_CONF_VERSION_MAJOR}.${_7BIT_CONF_VERSION_MINOR}.${_7BIT_CONF_VERSION_PATCH}) -project(7bitConf LANGUAGES CXX VERSION ${_7BIT_CONF_VERSION}) +project(${_7BIT_CONF_LIBRARY} LANGUAGES CXX VERSION ${_7BIT_CONF_VERSION}) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Cmake") +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Cmake") -include(Setup) -include(GNUInstallDirs) +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() -set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -include_directories(${CMAKE_SOURCE_DIR}/Include) +include(Setup) +include(GNUInstallDirs) + +include_directories(${_7BIT_CONF_INCLUDE_DIR}) add_subdirectory(Source) @@ -36,13 +41,13 @@ if (_7BIT_CONF_BUILD_SINGLE_HEADER) endif () if (_7BIT_CONF_INSTALL) - set(PROJECT_CONFIG_IN ${CMAKE_SOURCE_DIR}/Cmake/7bitConfConfig.cmake.in) - set(PROJECT_CONFIG_OUT ${CMAKE_BINARY_DIR}/7bitConfConfig.cmake) + set(PROJECT_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/Cmake/7bitConfConfig.cmake.in) + set(PROJECT_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/7bitConfConfig.cmake) set(CONFIG_TARGETS_FILE 7bitConfConfigTargets.cmake) set(VERSIONS_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/7bitConfConfigVersion.cmake) set(EXPORT_DEST_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/7bitConf) - install(DIRECTORY ${_7BIT_CONF_HEADERS_DIR}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(DIRECTORY ${_7BIT_CONF_INCLUDE_DIR}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( TARGETS 7bitConf diff --git a/Cmake/Setup.cmake b/Cmake/Setup.cmake index c7e3dc2..7a65552 100644 --- a/Cmake/Setup.cmake +++ b/Cmake/Setup.cmake @@ -22,16 +22,17 @@ set(CPACK_PACKAGE_VERSION_MAJOR ${_7BIT_CONF_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${_7BIT_CONF_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${_7BIT_CONF_VERSION_PATCH}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "7bitInjector is a simple C++ dependency injection library") -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") -set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") set(CPACK_SOURCE_GENERATOR "TGZ;ZIP") -set(_7BIT_CONF_HEADERS_DIR "${CMAKE_SOURCE_DIR}/Include") +set(_7BIT_CONF_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") +set(_7BIT_CONF_CONF_DIR "${_7BIT_CONF_INCLUDE_DIR}/SevenBit/Conf") -set(_7BIT_CONF_MAIN_HEADER "${_7BIT_CONF_HEADERS_DIR}/SevenBit/Conf.hpp") -file(GLOB _7BIT_CONF_TOP_HEADERS "${_7BIT_CONF_HEADERS_DIR}/SevenBit/Conf/*.hpp") -file(GLOB _7BIT_CONF_DETAILS_HEADERS "${_7BIT_CONF_HEADERS_DIR}/SevenBit/Conf/Details/*.hpp") -file(GLOB _7BIT_CONF_IMPL_HEADERS "${_7BIT_CONF_HEADERS_DIR}/SevenBit/Conf/Impl/*.hpp") +set(_7BIT_CONF_MAIN_HEADER "${_7BIT_CONF_INCLUDE_DIR}/SevenBit/Conf.hpp") +file(GLOB _7BIT_CONF_TOP_HEADERS "${_7BIT_CONF_CONF_DIR}/*.hpp") +file(GLOB _7BIT_CONF_DETAILS_HEADERS "${_7BIT_CONF_CONF_DIR}/Details/*.hpp") +file(GLOB _7BIT_CONF_IMPL_HEADERS "${_7BIT_CONF_CONF_DIR}/Impl/*.hpp") set(_7BIT_CONF_ALL_HEADERS ${_7BIT_CONF_MAIN_HEADER} ${_7BIT_CONF_TOP_HEADERS} ${_7BIT_CONF_DETAILS_HEADERS} ${_7BIT_CONF_IMPL_HEADERS}) source_group("Header Files\\SevenBit" FILES ${_7BIT_CONF_TOP_HEADERS}) @@ -64,12 +65,22 @@ else () set(_7BIT_CONF_STATIC_LIB true) endif () -configure_file(${CMAKE_SOURCE_DIR}/Include/SevenBit/Conf/CmakeDef.hpp.input ${CMAKE_SOURCE_DIR}/Include/SevenBit/Conf/CmakeDef.hpp) +configure_file(${_7BIT_CONF_CONF_DIR}/CmakeDef.hpp.input ${_7BIT_CONF_CONF_DIR}/CmakeDef.hpp) + +set(BYTE_SIZE 8) +math(EXPR MEMORY_SIZE "${CMAKE_SIZEOF_VOID_P} * ${BYTE_SIZE}") set(INFOS - "${CMAKE_PROJECT_NAME} version: ${_7BIT_CONF_VERSION}" - "${CMAKE_PROJECT_NAME} build type: ${CMAKE_BUILD_TYPE} " - "${CMAKE_PROJECT_NAME} build as ${_7BIT_CONF_BUILD_LIBRARY_TYPE} library" + "${_7BIT_CONF_LIBRARY} ${_7BIT_CONF_VERSION}" + "Build type: ${CMAKE_BUILD_TYPE}" + "Library type: ${_7BIT_CONF_BUILD_LIBRARY_TYPE}" + "==================================================" + "Cmake version: ${CMAKE_VERSION}" + "Os: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}" + "Architecture: ${CMAKE_SYSTEM_PROCESSOR} ${MEMORY_SIZE}bit" + "CXX compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" + "CXX standard: ${CMAKE_CXX_STANDARD}" + "Generator: ${CMAKE_GENERATOR}" "==================================================" "Build tests: ${_7BIT_CONF_BUILD_TESTS}" "Build examples: ${_7BIT_CONF_BUILD_EXAMPLES}" diff --git a/Include/SevenBit/Conf/CmakeDef.hpp b/Include/SevenBit/Conf/CmakeDef.hpp index b4115c4..077e658 100644 --- a/Include/SevenBit/Conf/CmakeDef.hpp +++ b/Include/SevenBit/Conf/CmakeDef.hpp @@ -14,9 +14,9 @@ #define _7BIT_CONF_VERSION_MAJOR 1 #define _7BIT_CONF_VERSION_MINOR 1 -/* #undef _7BIT_CONF_VERSION_PATCH */ +#define _7BIT_CONF_VERSION_PATCH 1 -#define _7BIT_CONF_VERSION "1.1.0" +#define _7BIT_CONF_VERSION "1.1.1" #ifndef _7BIT_CONF_VERSION_MAJOR #define _7BIT_CONF_VERSION_MAJOR 0 diff --git a/SingleHeader/CMakeLists.txt b/SingleHeader/CMakeLists.txt index b1cf051..8da7d25 100644 --- a/SingleHeader/CMakeLists.txt +++ b/SingleHeader/CMakeLists.txt @@ -5,7 +5,7 @@ set(_7BIT_CONF_SINGLE_OUT ${CMAKE_CURRENT_BINARY_DIR}/SevenBitConf.hpp) add_custom_command(OUTPUT ${_7BIT_CONF_SINGLE_OUT} COMMAND - ${QUOM_EXECUTABLE} ${_7BIT_CONF_SINGLE_IN} ${_7BIT_CONF_SINGLE_OUT} -I ${_7BIT_CONF_HEADERS_DIR} + ${QUOM_EXECUTABLE} ${_7BIT_CONF_SINGLE_IN} ${_7BIT_CONF_SINGLE_OUT} -I ${_7BIT_CONF_INCLUDE_DIR} DEPENDS ${_7BIT_CONF_ALL_HEADERS} COMMENT "Generating single header with Quom") diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 0957235..99897b8 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -1,6 +1,18 @@ include(GenerateExportHeader) +find_package(taocpp-json QUIET) +if(NOT taocpp-json_FOUND) + include(FetchContent) + + FetchContent_Declare( + taocpp-json + GIT_REPOSITORY https://github.com/taocpp/json.git + GIT_TAG 1.0.0-beta.14 + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(taocpp-json) +endif() find_package(taocpp-json REQUIRED) if (_7BIT_CONF_SHARED_LIB) @@ -17,20 +29,20 @@ endif () if (_7BIT_CONF_HEADER_ONLY_LIB) target_link_libraries(7bitConf INTERFACE - taocpp::json + taocpp-json ) else () target_link_libraries(7bitConf - taocpp::json + taocpp-json ) endif () add_library(7bitConf::7bitConf ALIAS 7bitConf) -target_include_directories( - 7bitConf INTERFACE - $ - $) - set_target_properties(7bitConf PROPERTIES VERSION ${_7BIT_CONF_VERSION}) set_target_properties(7bitConf PROPERTIES DEBUG_POSTFIX d) + +string(REPLACE ";" "$" dirs "${_7BIT_CONF_INCLUDE_DIR}") +target_include_directories(7bitConf INTERFACE + $ + "$/${CMAKE_INSTALL_INCLUDEDIR}>") diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 6208239..8be0b0f 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,4 +1,14 @@ -find_package(GTest REQUIRED) + +set(gtest_build_tests OFF) +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp) From eaf0fbfdb86b4edb6263066da2258871fb98384c Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Mon, 19 Feb 2024 18:06:32 +0100 Subject: [PATCH 02/31] refactor, remove conan lib provider --- Tests/AppSettingsConfigurationTest.cpp | 48 --- Tests/CMakeLists.txt | 31 +- Tests/ChainedConfigurationTest.cpp | 80 ----- Tests/Classes/CustomConfigSource.hpp | 17 -- Tests/CommandLineConfigurationTest.cpp | 93 ------ Tests/ConfigurationBuilderTest.cpp | 91 ------ Tests/ConfigurationTest.cpp | 94 ------ Tests/DeserializersTest.cpp | 237 --------------- Tests/EnvironmentVarsConfigurationTest.cpp | 97 ------ Tests/Files/Directory/settingOne.json | 9 - Tests/Files/Directory/settingTwo.json | 10 - Tests/Files/appsettings.dev.json | 10 - Tests/Files/appsettings.json | 9 - Tests/Files/bad.json | 1 - Tests/InMemoryConfigurationTest.cpp | 39 --- Tests/JsonConfigurationTest.cpp | 37 --- Tests/JsonFileConfigurationTest.cpp | 62 ---- Tests/JsonObjectExtTest.cpp | 281 ------------------ Tests/JsonStreamConfigurationTest.cpp | 66 ----- Tests/KeyPerFileConfigurationTest.cpp | 87 ------ Tests/MapConfigutationTest.cpp | 44 --- Tests/Mocks/ConfigurationBuilderMock.hpp | 16 - Tests/Mocks/DeserializerMock.hpp | 12 - Tests/Mocks/SettingSplitterMock.hpp | 11 - Tests/Mocks/ValueDeserializersMapMock.hpp | 10 - Tests/RunTests.cpp | 10 - Tests/SettingParserBuilderTest.cpp | 108 ------- Tests/SettingParserTest.cpp | 182 ------------ Tests/SettingSplitterTest.cpp | 113 ------- Tests/TestTemplate.cpp | 20 -- Tests/Utilities/ParamsTest.hpp | 53 ---- Tests/UtilsTest.cpp | 327 --------------------- 32 files changed, 5 insertions(+), 2300 deletions(-) delete mode 100644 Tests/AppSettingsConfigurationTest.cpp delete mode 100644 Tests/ChainedConfigurationTest.cpp delete mode 100644 Tests/Classes/CustomConfigSource.hpp delete mode 100644 Tests/CommandLineConfigurationTest.cpp delete mode 100644 Tests/ConfigurationBuilderTest.cpp delete mode 100644 Tests/ConfigurationTest.cpp delete mode 100644 Tests/DeserializersTest.cpp delete mode 100644 Tests/EnvironmentVarsConfigurationTest.cpp delete mode 100644 Tests/Files/Directory/settingOne.json delete mode 100644 Tests/Files/Directory/settingTwo.json delete mode 100644 Tests/Files/appsettings.dev.json delete mode 100644 Tests/Files/appsettings.json delete mode 100644 Tests/Files/bad.json delete mode 100644 Tests/InMemoryConfigurationTest.cpp delete mode 100644 Tests/JsonConfigurationTest.cpp delete mode 100644 Tests/JsonFileConfigurationTest.cpp delete mode 100644 Tests/JsonObjectExtTest.cpp delete mode 100644 Tests/JsonStreamConfigurationTest.cpp delete mode 100644 Tests/KeyPerFileConfigurationTest.cpp delete mode 100644 Tests/MapConfigutationTest.cpp delete mode 100644 Tests/Mocks/ConfigurationBuilderMock.hpp delete mode 100644 Tests/Mocks/DeserializerMock.hpp delete mode 100644 Tests/Mocks/SettingSplitterMock.hpp delete mode 100644 Tests/Mocks/ValueDeserializersMapMock.hpp delete mode 100644 Tests/RunTests.cpp delete mode 100644 Tests/SettingParserBuilderTest.cpp delete mode 100644 Tests/SettingParserTest.cpp delete mode 100644 Tests/SettingSplitterTest.cpp delete mode 100644 Tests/TestTemplate.cpp delete mode 100644 Tests/Utilities/ParamsTest.hpp delete mode 100644 Tests/UtilsTest.cpp diff --git a/Tests/AppSettingsConfigurationTest.cpp b/Tests/AppSettingsConfigurationTest.cpp deleted file mode 100644 index a68fcdc..0000000 --- a/Tests/AppSettingsConfigurationTest.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/AppSettingsConfiguration.hpp" - -class AppSettingsConfiguration : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - AppSettingsConfiguration() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(AppSettingsConfiguration, ShouldLoadAppSettings) -{ - auto provider = sb::cf::AppSettingsConfigurationSource::create()->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, - {"MySetting", "appsettings.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(AppSettingsConfiguration, ShouldLoadDevAppSettings) -{ - auto provider = sb::cf::AppSettingsConfigurationSource::create("dev")->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, - {"MySetting", "appsettings.dev.json Value"}, - {"ExtraSetting", "extra appsettings.dev.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 8be0b0f..e59daed 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,35 +1,14 @@ - -set(gtest_build_tests OFF) include(FetchContent) - FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.14.0 + GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # release-1.14.0 ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp) - -add_executable(Tests - ${SOURCES} -) - -target_link_libraries(Tests PUBLIC - GTest::gtest - GTest::gmock - 7bitConf -) - -file(GLOB_RECURSE FILES CONFIGURE_DEPENDS Files/*) -file(GLOB_RECURSE FILES_DIR CONFIGURE_DEPENDS Files/Directory/*) - -file(COPY ${FILES} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -file(COPY ${FILES_DIR} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Directory) - -file(COPY ${FILES} DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY ${FILES_DIR} DESTINATION ${CMAKE_BINARY_DIR}/Directory) +enable_testing() -gtest_discover_tests(Tests - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +add_subdirectory(EndToEnd) +add_subdirectory(Integration) +add_subdirectory(Unit) \ No newline at end of file diff --git a/Tests/ChainedConfigurationTest.cpp b/Tests/ChainedConfigurationTest.cpp deleted file mode 100644 index cc123b8..0000000 --- a/Tests/ChainedConfigurationTest.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/ChainedConfiguration.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonConfiguration.hpp" -#include "SevenBit/Conf/JsonFileConfiguration.hpp" - -class ChainedConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - ChainedConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(ChainedConfigurationTest, ShouldLoadEmptyChainConfig) -{ - auto provider = sb::cf::ChainedConfigurationSource::create()->build(mock); - - provider->load(); - - EXPECT_TRUE(provider->getConfiguration().empty()); -} - -TEST_F(ChainedConfigurationTest, ShouldFailCreationDueToNullSource) -{ - EXPECT_THROW(auto result = sb::cf::ChainedConfigurationSource::create({nullptr}), sb::cf::NullPointerException); -} - -TEST_F(ChainedConfigurationTest, ShouldFailAddDueToNullSource) -{ - auto source = - sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json")}); - - EXPECT_THROW(source->add(nullptr), sb::cf::NullPointerException); -} - -TEST_F(ChainedConfigurationTest, ShouldLoadSimpleChainedConfig) -{ - auto provider = - sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json")}) - ->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, - {"MySetting", "appsettings.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(ChainedConfigurationTest, ShouldLoadComplexChainedConfig) -{ - auto provider = - sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json"), - sb::cf::JsonFileConfigurationSource::create("appsettings.dev.json"), - sb::cf::JsonConfigurationSource::create({{"number", 1}})}) - ->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, - {"number", 1}, - {"MySetting", "appsettings.dev.json Value"}, - {"ExtraSetting", "extra appsettings.dev.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} diff --git a/Tests/Classes/CustomConfigSource.hpp b/Tests/Classes/CustomConfigSource.hpp deleted file mode 100644 index c581dcf..0000000 --- a/Tests/Classes/CustomConfigSource.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "SevenBit/Conf/IConfigurationBuilder.hpp" -#include "SevenBit/Conf/IConfigurationProvider.hpp" -#include "SevenBit/Conf/IConfigurationSource.hpp" -#include "SevenBit/Conf/JsonConfiguration.hpp" -#include "SevenBit/Conf/ObjectHolder.hpp" - -class CustomConfigSource : public sb::cf::IConfigurationSource -{ - sb::cf::IConfigurationProvider::Ptr build(sb::cf::IConfigurationBuilder &builder) - { - sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get()++; - - return sb::cf::JsonConfigurationSource::create({})->build(builder); - } -}; diff --git a/Tests/CommandLineConfigurationTest.cpp b/Tests/CommandLineConfigurationTest.cpp deleted file mode 100644 index e726e67..0000000 --- a/Tests/CommandLineConfigurationTest.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/CommandLineConfiguration.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - -class CommandLineConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - CommandLineConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) -{ - std::vector args; - EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), - sb::cf::NullPointerException); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) -{ - const char *const argv[] = { - "program/path", "--string___string=test", "--list:0!string=string", "double!double=1.22", - "true_bool!bool=-1", "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", - "--list:1=string1", "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", - "--int_list:1___int=22", "int_list__2!int=11", - }; - int size = sizeof(argv) / sizeof(char *); - auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"string", "test"}, - {"double", 1.22}, - {"false_bool", false}, - {"false_bool2", false}, - {"true_bool", true}, - {"true_bool2", true}, - {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"int_list", sb::cf::JsonArray{33, 22, 11}}, - {"object", {{"inner", {{"object", "string"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) -{ - std::vector args = { - "--string___string=test", "--list:0!string=string", "double!double=1.22", "true_bool!bool=-1", - "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", "--list:1=string1", - "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", "--int_list:1___int=22", - "int_list__2!int=11", - }; - auto provider = sb::cf::CommandLineConfigurationSource::create(args)->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"string", "test"}, - {"double", 1.22}, - {"false_bool", false}, - {"false_bool2", false}, - {"true_bool", true}, - {"true_bool2", true}, - {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"int_list", sb::cf::JsonArray{33, 22, 11}}, - {"object", {{"inner", {{"object", "string"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) -{ - const char *argv[] = { - "exec/path", - }; - int size = sizeof(argv) / sizeof(char *); - auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); - - provider->load(); - - EXPECT_TRUE(provider->getConfiguration().empty()); -} diff --git a/Tests/ConfigurationBuilderTest.cpp b/Tests/ConfigurationBuilderTest.cpp deleted file mode 100644 index 838b1f0..0000000 --- a/Tests/ConfigurationBuilderTest.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#include "Classes/CustomConfigSource.hpp" -#include "SevenBit/Conf/ConfigurationBuilder.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/ObjectHolder.hpp" - -class ConfigurationBuilderTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - ConfigurationBuilderTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(ConfigurationBuilderTest, ShouldFailCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::ConfigurationBuilder{{nullptr}}, sb::cf::NullPointerException); -} - -TEST_F(ConfigurationBuilderTest, ShouldFailAddDueToNullSource) -{ - auto builder = sb::cf::ConfigurationBuilder{}; - - EXPECT_THROW(builder.add(nullptr), sb::cf::NullPointerException); -} - -TEST_F(ConfigurationBuilderTest, ShouldFailBuildDueToNullSource) -{ - auto builder = sb::cf::ConfigurationBuilder{}; - - builder.getSources().emplace_back(nullptr); - - EXPECT_THROW(builder.build(), sb::cf::NullPointerException); -} - -TEST_F(ConfigurationBuilderTest, ShouldClearSources) -{ - auto builder = sb::cf::ConfigurationBuilder{}; - - builder.addAppSettings("dev"); - - EXPECT_FALSE(builder.getSources().empty()); - - builder.clear(); - - EXPECT_TRUE(builder.getSources().empty()); -} - -TEST_F(ConfigurationBuilderTest, ShouldBuildSimpleConfig) -{ - auto builder = sb::cf::ConfigurationBuilder{}; - - builder.addAppSettings("dev") - .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "array!json=[3,2,1]"}) - .AddInMemory("set:set", 44444) - .addKeyPerFile("Directory"); - - EXPECT_NO_THROW(builder.build()); -} - -TEST_F(ConfigurationBuilderTest, ShouldBuildConfigWithProperties) -{ - auto builder = sb::cf::ConfigurationBuilder{}; - - builder.getProperties()["counter"] = sb::cf::ObjectHolder::from(0); - - auto conf = builder.addAppSettings("dev") - .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "array=3,2,1"}) - .AddInMemory("set:set", 44444) - .addKeyPerFile("Directory") - .add(std::make_unique()) - .add(std::make_unique()) - .add(std::make_unique()) - .add(std::make_unique()) - .build(); - - auto cnt = sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get(); - - EXPECT_EQ(cnt, 4); -} diff --git a/Tests/ConfigurationTest.cpp b/Tests/ConfigurationTest.cpp deleted file mode 100644 index 3b9989c..0000000 --- a/Tests/ConfigurationTest.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include - -#include "SevenBit/Conf/ConfigurationBuilder.hpp" - -class ConfigurationTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - ConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(ConfigurationTest, ShouldLoadConfig) -{ - auto conf = sb::cf::ConfigurationBuilder{} - .addAppSettings("dev") - .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "Array:0!int=33"}) - .AddInMemory("set:set", 44444) - .addKeyPerFile("Directory") - .build(); - - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{33, 2, 3, 4, 5}}, - {"MySetting", "appsettings.dev.json Value"}, - {"ExtraSetting", "extra appsettings.dev.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}, - {"set", {{"set", 44444}}}, - {"string", "2"}, - {"settingOne", - {{"number", 12345}, - {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, - {"string", "string"}, - {"object", {{"num", 134}, {"string", "string"}}}}}, - {"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; - - EXPECT_EQ(conf->root(), expected); -} - -TEST_F(ConfigurationTest, ShouldFindConfgValues) -{ - auto conf = sb::cf::ConfigurationBuilder{} - .addAppSettings("dev") - .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "Array:0!int=33"}) - .AddInMemory("set:set", 44444) - .addKeyPerFile("Directory") - .build(); - - EXPECT_TRUE(conf->find("Array")); - EXPECT_TRUE(conf->find("MySetting")); - EXPECT_TRUE(conf->deepFind("settingOne:number")); - EXPECT_TRUE(conf->deepFind({"settingTwo", "array", "0"})); - EXPECT_TRUE(conf->deepFind("settingTwo:array:0")); - EXPECT_EQ(conf->at("Array"), (sb::cf::JsonArray{33, 2, 3, 4, 5})); - EXPECT_EQ(conf->deepAt("settingOne:number"), (std::int64_t{12345})); - EXPECT_EQ(conf->deepAt("settingTwo:array:0"), (std::int64_t{1})); - EXPECT_EQ(conf->operator[]("settingTwo:array:0"), (std::int64_t{1})); - EXPECT_EQ(conf->operator[]({"settingTwo", "array", "0"}), (std::int64_t{1})); -} - -TEST_F(ConfigurationTest, ShouldSerializeValues) -{ - auto conf = sb::cf::ConfigurationBuilder{} - .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "Array:0!int=33"}) - .AddInMemory("set:set", 44444) - .build(); - - std::string expected = R"({"Array":[33,2,3,4,5],"MySetting":""})"; - std::ostringstream stream; - - EXPECT_EQ(conf->toString(), R"({ - "Array": [ - 33 - ], - "set": { - "set": 44444 - }, - "string": "2" -})"); - EXPECT_EQ(conf->toString(0, ""), R"({"Array": [33],"set": {"set": 44444},"string": "2"})"); - stream << *conf; - EXPECT_EQ(stream.str(), R"({"Array":[33],"set":{"set":44444},"string":"2"})"); -} diff --git a/Tests/DeserializersTest.cpp b/Tests/DeserializersTest.cpp deleted file mode 100644 index 28dc776..0000000 --- a/Tests/DeserializersTest.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -#include "Utilities/ParamsTest.hpp" - -class DeserializersTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - DeserializersTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap() -{ - sb::cf::details::ValueDeserializersMap deserializers; - deserializers.set("string", std::make_unique()); - deserializers.set("bool", std::make_unique()); - deserializers.set("int", std::make_unique()); - deserializers.set("double", std::make_unique()); - deserializers.set("uint", std::make_unique()); - deserializers.set("json", std::make_unique()); - deserializers.set("null", std::make_unique()); - return std::move(deserializers); -} - -static Params, sb::cf::JsonValue> DeserializeData = { - // Int - {"int", "12", std::int64_t{12}}, - {"int", "-12", std::int64_t{-12}}, - {"Int", "-12", std::int64_t{-12}}, - {"INT", "-12", std::int64_t{-12}}, - {"InT", "-12", std::int64_t{-12}}, - {"int", std::nullopt, std::int64_t{0}}, - {"int", "1", std::int64_t{1}}, - {"int", " 1", std::int64_t{1}}, - {"int", "\t\n 1", std::int64_t{1}}, - {"int", "1", std::int64_t{1}}, - {"Int", "1", std::int64_t{1}}, - {"InT", "1", std::int64_t{1}}, - {"INT", "1", std::int64_t{1}}, - {"int", "-1234", std::int64_t{-1234}}, - {"int", "1234", std::int64_t{1234}}, - {"int", "-223372036854775807", std::int64_t{-223372036854775807ll}}, - {"int", "223372036854775807", std::int64_t{223372036854775807ll}}, - - // UInt - {"uint", "12", std::uint64_t{12}}, - {"Uint", "12", std::uint64_t{12}}, - {"UINT", "12", std::uint64_t{12}}, - {"UiNt", "12", std::uint64_t{12}}, - {"uint", "1222", std::uint64_t{1222}}, - {"uint", std::nullopt, std::uint64_t{0}}, - {"uint", "1", std::uint64_t{1}}, - {"uint", " 1", std::uint64_t{1}}, - {"uint", "\t\n 1", std::uint64_t{1}}, - {"uInt", "1", std::uint64_t{1}}, - {"uInT", "1", std::uint64_t{1}}, - {"UINT", "1", std::uint64_t{1}}, - {"uint", "1234", std::uint64_t{1234}}, - {"uint", "223372036854775807", std::uint64_t{223372036854775807ll}}, - {"uint", "17446744073709551615", std::uint64_t{17446744073709551615ull}}, - - // Bool - {"bool", "true", true}, - {"BoOl", "true", true}, - {"bool", "1", true}, - {"bool", "-11", true}, - {"bool", "0", false}, - {"Bool", "true", true}, - {"BOOL", "true", true}, - {"bool", "True", true}, - {"bool", "TrUe", true}, - {"bool", " 1", true}, - {"bool", "\t\n 1", true}, - {"bool", "-1", true}, - {"bool", "-1123", true}, - {"bool", "1123", true}, - {"bool", "false", false}, - {"bool", "False", false}, - {"bool", "FAlSe", false}, - {"bool", std::nullopt, false}, - // String - {"string", "hello", std::string{"hello"}}, - {"String", "hello", std::string{"hello"}}, - {"STRING", "hello", std::string{"hello"}}, - {"STRING", "hello", std::string{"hello"}}, - {"String", "hello", std::string{"hello"}}, - {"StRinG", "hell\to", std::string{"hell\to"}}, - {"string", "hello", std::string{"hello"}}, - {"string", "", std::string{""}}, - {"string", std::nullopt, std::string{""}}, - // Double - {"double", "1.1", 1.1}, - {"Double", "1.1", 1.1}, - {"DOUBLE", "1.1", 1.1}, - {"DoUBle", "1.1", 1.1}, - {"double", "-11.1", -11.1}, - {"double", "1", 1.0}, - {"double", " 1", 1.0}, - {"double", "\t\n 1", 1.0}, - {"Double", "1", 1.0}, - {"dOuBle", "1", 1.0}, - {"DOUBLE", "1", 1.0}, - {"double", "1.123", 1.123}, - {"double", "-12.21", -12.21}, - {"double", "10000.11", 10000.11}, - {"double", "-10000.11", -10000.11}, - {"double", std::nullopt, 0.0}, - // Null - {"null", "hello", sb::cf::json::null}, - {"Null", "hello", sb::cf::json::null}, - {"NULL", "hello", sb::cf::json::null}, - {"nULl", "hello", sb::cf::json::null}, - {"null", "1", sb::cf::json::null}, - {"null", "asdqwdwq", sb::cf::json::null}, - {"Null", "1", sb::cf::json::null}, - {"NULL", "1asdqwdwq", sb::cf::json::null}, - {"NuLl", "1.123 asdqwdwq", sb::cf::json::null}, - {"null", std::nullopt, sb::cf::json::null}, - // Json - {"json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"Json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"JSON", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"jSOn", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"JSon", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"JsOn", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"json", R"({"hello": [1]})", sb::cf::JsonObject{{"hello", sb::cf::JsonArray{1}}}}, - {"json", R"({"hello": null})", sb::cf::JsonObject{{"hello", sb::cf::json::null}}}, - -}; -PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) -{ - const auto &[type, value, expected] = GetParam(); - auto deserializers = makeDefaultDeserializersMap(); - - auto deserializer = deserializers.getDeserializerFor(type); - EXPECT_TRUE(deserializer); - EXPECT_EQ(deserializer->deserialize(value), expected); -} - -TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) -{ - auto deserializers = makeDefaultDeserializersMap(); - - auto deserializer = deserializers.getDeserializerFor("json"); - EXPECT_EQ((deserializer->deserialize(std::nullopt)), (sb::cf::JsonValue{})); -} - -TEST_F(DeserializersTest, ShouldNotFoundDeserializer) -{ - auto deserializers = makeDefaultDeserializersMap(); - - deserializers.set("unknown2", nullptr); - - EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); - EXPECT_FALSE(deserializers.getDeserializerFor("unknown")); - EXPECT_FALSE(deserializers.getDeserializerFor("unknown2")); -} - -static Params> FailDeserializeValues = { - // Int - {"int", "123 abcd"}, - {"int", "123 "}, - {"int", "123.123"}, - {"int", " as 123 abcd"}, - {"int", "abcd"}, - {"int", " "}, - {"int", ""}, - {"int", "\n"}, - // UInt - {"uint", "123 abcd"}, - {"uint", "123 "}, - {"uint", "-12"}, - {"uint", "123.123"}, - {"uint", " as 123 abcd"}, - {"uint", "abcd"}, - {"uint", " "}, - {"uint", ""}, - {"uint", "\n"}, - // Bool - {"bool", "123 abcd"}, - {"bool", "123.123"}, - {"bool", " as 123 abcd"}, - {"bool", "abcd"}, - {"bool", " "}, - {"bool", ""}, - {"bool", "\n"}, - {"bool", "ttruee"}, - {"bool", "ffalsee"}, - {"bool", "false "}, - {"bool", "false "}, - {"bool", " false"}, - // Double - {"double", "123 abcd"}, - {"double", "123 "}, - {"double", "123 "}, - {"double", " as 123 abcd"}, - {"double", "abcd"}, - {"double", " "}, - {"double", ""}, - {"double", "\n"}, - // Json - {"json", "123 abcd"}, - {"json", " as 123 abcd"}, - {"json", "abcd"}, - {"json", "{\"hello: 123}"}, - {"json", "{\"hello\": sde123}"}, - {"json", " "}, - {"json", ""}, - {"json", "\n"}, -}; -PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) -{ - const auto &[type, value] = GetParam(); - auto deserializers = makeDefaultDeserializersMap(); - - auto deserializer = deserializers.getDeserializerFor(type); - - EXPECT_TRUE(deserializer); - EXPECT_ANY_THROW(auto result = deserializer->deserialize(value)); -} diff --git a/Tests/EnvironmentVarsConfigurationTest.cpp b/Tests/EnvironmentVarsConfigurationTest.cpp deleted file mode 100644 index 8a3d39d..0000000 --- a/Tests/EnvironmentVarsConfigurationTest.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/EnvironmentVarsConfiguration.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - -#ifdef _WIN32 -#define _7BIT_CONF_PUT_ENV _putenv -#else -#define _7BIT_CONF_PUT_ENV putenv -#endif - -class EnvironmentVarsConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - EnvironmentVarsConfigurationTest() - { - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING=test"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_DOUBLE___double=1.4"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_BOOL___bool=true"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING_LIST__0=string"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING_LIST__1=string1"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING_LIST__2=string2"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__0___int=1"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__1___int=3"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__2___int=22"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_JSON___json={\"key\": \"value\"}"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_OBJECT__INNER__OBJECT=string"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_OTHER_CONFIG_TEST_STRING=string2"); - } - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(EnvironmentVarsConfigurationTest, ShouldFailCreationDueToNullParser) -{ - EXPECT_THROW(auto result = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_", nullptr), - sb::cf::NullPointerException); -} - -TEST_F(EnvironmentVarsConfigurationTest, ShouldFailProviderCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::EnvironmentVarsConfigurationProvider(nullptr), sb::cf::NullPointerException); -} - -TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVars) -{ - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_")->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"TEST_STRING", "test"}, - {"TEST_DOUBLE", 1.4}, - {"TEST_BOOL", true}, - {"TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, - {"TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, - {"TEST_JSON", {{"key", "value"}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVarsWithPrefix) -{ - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_")->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"CONFIG_TEST_STRING", "test"}, - {"CONFIG_TEST_DOUBLE", 1.4}, - {"CONFIG_TEST_BOOL", true}, - {"OTHER_CONFIG_TEST_STRING", "string2"}, - {"CONFIG_TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, - {"CONFIG_TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"CONFIG_TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, - {"CONFIG_TEST_JSON", {{"key", "value"}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(EnvironmentVarsConfigurationTest, ShouldNotLoadConfFromEnvVars) -{ - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BITCONFIGURATION_")->build(mock); - - provider->load(); - - EXPECT_TRUE(provider->getConfiguration().empty()); -} diff --git a/Tests/Files/Directory/settingOne.json b/Tests/Files/Directory/settingOne.json deleted file mode 100644 index aab360d..0000000 --- a/Tests/Files/Directory/settingOne.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "number": 12345, - "array": [1, 2, 3, 4, 5, 6], - "string": "string", - "object": { - "num": 134, - "string": "string" - } -} diff --git a/Tests/Files/Directory/settingTwo.json b/Tests/Files/Directory/settingTwo.json deleted file mode 100644 index cf81edc..0000000 --- a/Tests/Files/Directory/settingTwo.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "array": [1], - "string": "stringdev", - "object": { - "string": "stringdev", - "inner": { - "num": 12345 - } - } -} diff --git a/Tests/Files/appsettings.dev.json b/Tests/Files/appsettings.dev.json deleted file mode 100644 index f82d6e9..0000000 --- a/Tests/Files/appsettings.dev.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Array": [11], - "MySetting": "appsettings.dev.json Value", - "ExtraSetting": "extra appsettings.dev.json Value", - "Logging": { - "LogLevel": { - "Default": "Warning" - } - } -} diff --git a/Tests/Files/appsettings.json b/Tests/Files/appsettings.json deleted file mode 100644 index 38aa15b..0000000 --- a/Tests/Files/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Array": [1, 2, 3, 4, 5], - "MySetting": "appsettings.json Value", - "Logging": { - "LogLevel": { - "Default": "Information" - } - } -} diff --git a/Tests/Files/bad.json b/Tests/Files/bad.json deleted file mode 100644 index e5c73e4..0000000 --- a/Tests/Files/bad.json +++ /dev/null @@ -1 +0,0 @@ -"bad" diff --git a/Tests/InMemoryConfigurationTest.cpp b/Tests/InMemoryConfigurationTest.cpp deleted file mode 100644 index 6840938..0000000 --- a/Tests/InMemoryConfigurationTest.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/InMemoryConfiguration.hpp" - -class InMemoryConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - InMemoryConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(InMemoryConfigurationTest, ShouldFailProviderCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::InMemoryConfigurationProvider(nullptr), sb::cf::NullPointerException); -} - -TEST_F(InMemoryConfigurationTest, ShouldLoadSimpleSettingConfiguration) -{ - auto provider = - sb::cf::InMemoryConfigurationSource::create({{"yes:yes:inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}})->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"yes", {{"yes", {{"inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} diff --git a/Tests/JsonConfigurationTest.cpp b/Tests/JsonConfigurationTest.cpp deleted file mode 100644 index 6132fcd..0000000 --- a/Tests/JsonConfigurationTest.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonConfiguration.hpp" - -class JsonConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - JsonConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(JsonConfigurationTest, ShouldFailProviderCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::JsonConfigurationProvider(nullptr), sb::cf::NullPointerException); -} - -TEST_F(JsonConfigurationTest, ShouldLoadSimpleJsonConfig) -{ - auto provider = sb::cf::JsonConfigurationSource::create({{"hello", 12345}})->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"hello", 12345}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} diff --git a/Tests/JsonFileConfigurationTest.cpp b/Tests/JsonFileConfigurationTest.cpp deleted file mode 100644 index 8f25e2f..0000000 --- a/Tests/JsonFileConfigurationTest.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonFileConfiguration.hpp" - -class JsonFileConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - JsonFileConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(JsonFileConfigurationTest, ShouldFailProviderCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::JsonFileConfigurationProvider(nullptr), sb::cf::NullPointerException); -} - -TEST_F(JsonFileConfigurationTest, ShouldLoadSimpleJsonConfigFile) -{ - auto provider = sb::cf::JsonFileConfigurationSource::create("appsettings.json")->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, - {"MySetting", "appsettings.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(JsonFileConfigurationTest, ShouldNotLoadNonExistingJsonConfigFile) -{ - auto provider = sb::cf::JsonFileConfigurationSource::create("nonExisting.json", true)->build(mock); - - provider->load(); - - EXPECT_TRUE(provider->getConfiguration().empty()); -} - -TEST_F(JsonFileConfigurationTest, ShouldFailLoadingNonExistingJsonConfigFile) -{ - auto provider = sb::cf::JsonFileConfigurationSource ::create("nonExisting.json")->build(mock); - - EXPECT_THROW(provider->load(), sb::cf::ConfigFileNotFoundException); -} - -TEST_F(JsonFileConfigurationTest, ShouldFailLoadingBadJsonConfigFile) -{ - auto provider = sb::cf::JsonFileConfigurationSource ::create("bad.json")->build(mock); - - EXPECT_THROW(provider->load(), sb::cf::BadConfigFileException); -} diff --git a/Tests/JsonObjectExtTest.cpp b/Tests/JsonObjectExtTest.cpp deleted file mode 100644 index e65eae1..0000000 --- a/Tests/JsonObjectExtTest.cpp +++ /dev/null @@ -1,281 +0,0 @@ -#include -#include - -#include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "Utilities/ParamsTest.hpp" - -using namespace sb::cf::json; - -class JsonObjectExtTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - JsonObjectExtTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -Params FindData{ - {"str", true, "hello"}, - {"number", true, 123}, - {"array", true, sb::cf::JsonArray{{{"key", "value"}}}}, - {"inner", true, -123}, - {"nonExisting", false, tao::json::null}, -}; -PARAMS_TEST(JsonObjectExtTest, ShouldFing, FindData) -{ - sb::cf::JsonValue json = { - {"str", "hello"}, {"number", 123}, {"array", sb::cf::JsonArray{{{"key", "value"}}}}, {"inner", -123}}; - - auto &[keys, expectedFound, expectedValue] = GetParam(); - auto valuePtr = sb::cf::details::JsonExt::find(json, keys); - EXPECT_EQ(!!valuePtr, expectedFound); - if (valuePtr) - { - EXPECT_EQ(*valuePtr, expectedValue); - } -} - -Params DeepFindData{ - {"inner:inner:str", true, "hello2"}, - {"inner:number", true, 1231}, - {"array:0:key", true, "value"}, - {"array:1:key", true, 12}, - {"array:2:key", false, tao::json::null}, - {"array:0.123:key", false, tao::json::null}, - {"array:0 123:key", false, tao::json::null}, - {"array:0 apok:key", false, tao::json::null}, - {"array:-1:key", false, tao::json::null}, - {"array:9notNumber:key", false, tao::json::null}, - {"inner:inner:nonExisting", false, tao::json::null}, - {"inner:inner:str:nonExisting", false, tao::json::null}, - {"nonExisting", false, tao::json::null}, - {"inner::inner:str", false, tao::json::null}, - -}; -PARAMS_TEST(JsonObjectExtTest, ShouldDeepFing, DeepFindData) -{ - sb::cf::JsonValue json = {{"str", "hello"}, - {"number", 123}, - {"array", sb::cf::JsonArray{{{"key", "value"}}, {{"key", 12}}}}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - { - {"str", "hello2"}, - {"number", 1232}, - }}}}}; - - auto &[keys, expectedFound, expectedValue] = GetParam(); - auto valuePtr = sb::cf::details::JsonExt::deepFind(json, keys); - EXPECT_EQ(!!valuePtr, expectedFound); - if (valuePtr) - { - EXPECT_EQ(*valuePtr, expectedValue); - } -} - -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverride) -{ - sb::cf::JsonObject json = {{"str", "hello"}, - {"number", 123}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - { - {"str", "hello2"}, - {"number", 1232}, - }}}}}; - - sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:str") = "hello3"; - sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:inner:str") = "hello5"; - - sb::cf::JsonObject expectedJson = {{"str", "hello"}, - {"number", 123}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - {{"str", "hello3"}, - {"number", 1232}, - {"inner", - { - {"str", "hello5"}, - }}}}}}}; - - EXPECT_EQ(json, expectedJson); -} - -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideArrayElement) -{ - sb::cf::JsonObject json = {{"str", "hello"}}; - - sb::cf::details::JsonExt::deepGetOrOverride(json, "array:3:object") = "value"; - - sb::cf::JsonObject expected = { - {"str", "hello"}, - {"array", - sb::cf::JsonArray{sb::cf::JsonValue{}, sb::cf::JsonValue{}, sb::cf::JsonValue{}, {{"object", "value"}}}}, - }; - - EXPECT_EQ(json, expected); -} - -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideExistingArrayElement) -{ - sb::cf::JsonObject json = {{"str", "hello"}, {"array", sb::cf::JsonArray{1234, {{"second", "element"}}}}}; - - sb::cf::details::JsonExt::deepGetOrOverride(json, "array:3:object") = "value"; - - sb::cf::JsonObject expected = { - {"str", "hello"}, - {"array", sb::cf::JsonArray{1234, {{"second", "element"}}, sb::cf::JsonValue{}, {{"object", "value"}}}}, - }; - - EXPECT_EQ(json, expected); -} - -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideWrongArrayElement) -{ - sb::cf::JsonObject json = {{"str", "hello"}}; - - sb::cf::details::JsonExt::deepGetOrOverride(json, "array:-3:object") = "value"; - - sb::cf::JsonObject expected = {{"str", "hello"}, {"array", {{"-3", {{"object", "value"}}}}}}; - - EXPECT_EQ(json, expected); -} - -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideDestroy) -{ - sb::cf::JsonObject json = {{"str", "hello"}, - {"number", 123}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - { - {"str", "hello2"}, - {"number", 1232}, - }}}}}; - - sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:str:fail") = "value"; - - sb::cf::JsonObject expected = {{"str", "hello"}, - {"number", 123}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - { - {"str", {{"fail", "value"}}}, - {"number", 1232}, - }}}}}; - - EXPECT_EQ(json, expected); -} - -TEST_F(JsonObjectExtTest, ShouldFaildDeepGetOrOverrideEmpty) -{ - sb::cf::JsonObject json = {{"str", "hello"}}; - - EXPECT_ANY_THROW(sb::cf::details::JsonExt::deepGetOrOverride(json, std::vector{})); -} - -TEST_F(JsonObjectExtTest, SouldDeepMergeEmptyJsonValue) -{ - sb::cf::JsonValue json; - - sb::cf::JsonValue jsonOverride = {{"str", "helloOv"}}; - - sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); - - sb::cf::JsonValue expected = {{"str", "helloOv"}}; - - EXPECT_EQ(json, expected); -} - -TEST_F(JsonObjectExtTest, SouldDeepMergeJsonValue) -{ - - sb::cf::JsonValue json = {{"str", "hello"}, - {"number", 123}, - {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, - {"default", "default"}, - {"inner", - { - {"str", "hello1"}, - {"number", 1231}, - - }}}; - - sb::cf::JsonValue jsonOverride = {{"str", "helloOv"}, - {"default", sb::cf::json::uninitialized}, - {"array", sb::cf::JsonArray{3, {{"value", "value"}}, 1}}, - {"inner", - {{"number", 12313}, - {"inner", - { - {"str", "hello2Ov"}, - {"number", 12323}, - }}}}}; - - sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); - - sb::cf::JsonObject expectedJson = {{"str", "helloOv"}, - {"number", 123}, - {"array", sb::cf::JsonArray{3, {{"value", "value"}}, 1, 4, 5, 6}}, - {"default", "default"}, - {"inner", - {{"str", "hello1"}, - {"number", 12313}, - {"inner", - { - {"str", "hello2Ov"}, - {"number", 12323}, - }}}}}; - - EXPECT_EQ(json, expectedJson); -} - -TEST_F(JsonObjectExtTest, SouldDeepMergeJsonArray) -{ - - sb::cf::JsonArray json{{{"str", "hello"}}, - {{"number", 123}}, - sb::cf::JsonValue{}, - {{"inner", - { - {"str", "hello1"}, - {"number", 1231}, - - }}}}; - - sb::cf::JsonArray jsonOverride{ - {{"str", "hello22"}}, - sb::cf::JsonValue{}, - {{"number", 123}}, - }; - - sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); - - sb::cf::JsonArray expectedJson{{{"str", "hello22"}}, - {{"number", 123}}, - {{"number", 123}}, - {{"inner", - { - {"str", "hello1"}, - {"number", 1231}, - - }}}}; - - EXPECT_EQ(json, expectedJson); -} diff --git a/Tests/JsonStreamConfigurationTest.cpp b/Tests/JsonStreamConfigurationTest.cpp deleted file mode 100644 index 0ce74a6..0000000 --- a/Tests/JsonStreamConfigurationTest.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonStreamConfiguration.hpp" - -class JsonStreamConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - JsonStreamConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(JsonStreamConfigurationTest, ShouldFailProviderCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::JsonStreamConfigurationProvider(nullptr), sb::cf::NullPointerException); -} - -TEST_F(JsonStreamConfigurationTest, ShouldLoadConfigFromStream) -{ - std::stringstream stream; - - stream << "{\"hello\": 12345, \"string\": \"asdf\"}"; - - auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"hello", 12345}, {"string", "asdf"}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(JsonStreamConfigurationTest, ShouldFailLoadingConfigFromBadStream) -{ - std::stringstream stream; - - stream << "\"hello\""; - - auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); - - EXPECT_THROW(provider->load(), sb::cf::BadStreamException); -} - -TEST_F(JsonStreamConfigurationTest, ShouldFailLoadingDueToDoubleStreamRead) -{ - std::stringstream stream; - - stream << "{\"hello\": 12345, \"string\": \"asdf\"}"; - - auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); - - provider->load(); - - EXPECT_ANY_THROW(provider->load()); -} diff --git a/Tests/KeyPerFileConfigurationTest.cpp b/Tests/KeyPerFileConfigurationTest.cpp deleted file mode 100644 index ca3249b..0000000 --- a/Tests/KeyPerFileConfigurationTest.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/KeyPerFileConfiguration.hpp" - -class KeyPerFileConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - KeyPerFileConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(KeyPerFileConfigurationTest, ShouldFailLoadingNonExistingDirectory) -{ - auto source = sb::cf::KeyPerFileConfigurationSource::create("nonexisting"); - - EXPECT_ANY_THROW(source->build(mock)); -} - -TEST_F(KeyPerFileConfigurationTest, ShouldLoadNonExistingDirectory) -{ - auto provider = sb::cf::KeyPerFileConfigurationSource::create("nonexisting", true)->build(mock); - - provider->load(); - - EXPECT_TRUE(provider->getConfiguration().empty()); -} - -TEST_F(KeyPerFileConfigurationTest, ShouldLoadDirectoryConfig) -{ - auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory")->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"settingOne", - {{"number", 12345}, - {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, - {"string", "string"}, - {"object", {{"num", 134}, {"string", "string"}}}}}, - {"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConfigFiles) -{ - auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory", false, "settingOne")->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConditionConfigFiles) -{ - auto provider = - sb::cf::KeyPerFileConfigurationSource::create("Directory", false, [](const std::filesystem::path &path) { - return path.filename() == "settingOne.json"; - })->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} diff --git a/Tests/MapConfigutationTest.cpp b/Tests/MapConfigutationTest.cpp deleted file mode 100644 index 8d646e3..0000000 --- a/Tests/MapConfigutationTest.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonConfiguration.hpp" -#include "SevenBit/Conf/MapConfiguration.hpp" - -class MapConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - MapConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(MapConfigurationTest, ShouldFailProviderCreationDueToNullSource) -{ - EXPECT_THROW(sb::cf::MapConfigurationProvider(nullptr, nullptr), sb::cf::NullPointerException); -} - -TEST_F(MapConfigurationTest, ShouldMapSimpleConfiguration) -{ - auto provider = sb::cf::MapConfigurationSource::create(sb::cf::JsonConfigurationSource::create({{"yes", 12345}}), - [](sb::cf::JsonObject ob) { - ob["yes2"] = "yes"; - return std::move(ob); - }) - ->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"yes", 12345}, {"yes2", "yes"}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} diff --git a/Tests/Mocks/ConfigurationBuilderMock.hpp b/Tests/Mocks/ConfigurationBuilderMock.hpp deleted file mode 100644 index a2305bc..0000000 --- a/Tests/Mocks/ConfigurationBuilderMock.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#include "SevenBit/Conf/IConfigurationBuilder.hpp" - -struct ConfigurationBuilderMock : public sb::cf::IConfigurationBuilder -{ - MOCK_METHOD((sb::cf::IConfigurationBuilder &), add, (sb::cf::IConfigurationSource::SPtr), (override)); - MOCK_METHOD((std::unordered_map &), getProperties, (), (override)); - MOCK_METHOD((const std::unordered_map &), getProperties, (), (const override)); - MOCK_METHOD((std::vector &), getSources, (), (override)); - MOCK_METHOD((const std::vector &), getSources, (), (const override)); - MOCK_METHOD((sb::cf::IConfiguration::Ptr), build, (), (override)); - MOCK_METHOD((void), clear, (), (override)); -}; diff --git a/Tests/Mocks/DeserializerMock.hpp b/Tests/Mocks/DeserializerMock.hpp deleted file mode 100644 index 7d4f5f4..0000000 --- a/Tests/Mocks/DeserializerMock.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "SevenBit/Conf/IDeserializer.hpp" - -struct DeserializerMock : public sb::cf::IDeserializer -{ - MOCK_METHOD((sb::cf::JsonValue), deserialize, (std::optional), (const override)); -}; diff --git a/Tests/Mocks/SettingSplitterMock.hpp b/Tests/Mocks/SettingSplitterMock.hpp deleted file mode 100644 index 131e738..0000000 --- a/Tests/Mocks/SettingSplitterMock.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -#include "SevenBit/Conf/ISettingSplitter.hpp" - -struct SettingSplitterMock : public sb::cf::ISettingSplitter -{ - MOCK_METHOD((sb::cf::ISettingSplitter::Result), split, (std::string_view), (const override)); -}; diff --git a/Tests/Mocks/ValueDeserializersMapMock.hpp b/Tests/Mocks/ValueDeserializersMapMock.hpp deleted file mode 100644 index 1fe4350..0000000 --- a/Tests/Mocks/ValueDeserializersMapMock.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -#include "SevenBit/Conf/IValueDeserializersMap.hpp" - -struct ValueDeserializersMapMock : public sb::cf::IValueDeserializersMap -{ - MOCK_METHOD((const sb::cf::IDeserializer *), getDeserializerFor, (std::string_view), (const override)); -}; diff --git a/Tests/RunTests.cpp b/Tests/RunTests.cpp deleted file mode 100644 index 0bdcecc..0000000 --- a/Tests/RunTests.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include - -int main(int argc, char *argv[]) -{ - // Run a specific test only - // testing::GTEST_FLAG(filter) = "Template.*"; - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/Tests/SettingParserBuilderTest.cpp b/Tests/SettingParserBuilderTest.cpp deleted file mode 100644 index 52a39dc..0000000 --- a/Tests/SettingParserBuilderTest.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include "Mocks/DeserializerMock.hpp" -#include "Mocks/SettingSplitterMock.hpp" -#include "Mocks/ValueDeserializersMapMock.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -#include "SevenBit/Conf/SettingParserBuilder.hpp" - -class SettingParserBuilderTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - SettingParserBuilderTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(SettingParserBuilderTest, ShouldBuildDefault) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); -} - -TEST_F(SettingParserBuilderTest, ShouldUseCustomConfig) -{ - sb::cf::SettingParserBuilder builder; - - sb::cf::SettingParserConfig config; - config.defaultType = "int"; - config.throwOnUnknownType = false; - config.allowEmptyKeys = true; - auto parser = builder.useConfig(config).build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "int"); - EXPECT_TRUE(casted.getAllowEmptyKeys()); - EXPECT_FALSE(casted.getThrowOnUnknownType()); -} - -TEST_F(SettingParserBuilderTest, ShouldUseValueDeserializer) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.useDefaultValueDeserializers() - .useValueDeserializer("newType", std::make_unique()) - .build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); - EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("newType")); - EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("int")); - EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("bool")); -} - -TEST_F(SettingParserBuilderTest, ShouldUseCustomValueDeserializerMap) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); -} - -TEST_F(SettingParserBuilderTest, ShouldUseCustomSplitter) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.useSplitter(std::make_unique()).build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); -} diff --git a/Tests/SettingParserTest.cpp b/Tests/SettingParserTest.cpp deleted file mode 100644 index c4eba41..0000000 --- a/Tests/SettingParserTest.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include - -#include "Mocks/DeserializerMock.hpp" -#include "Mocks/SettingSplitterMock.hpp" -#include "Mocks/ValueDeserializersMapMock.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - -class SettingParserTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - SettingParserTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(SettingParserTest, ShouldParseSetting) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!int=123"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = 123; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_FALSE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullSplitter) -{ - sb::cf::ISettingSplitter::Ptr splitter; - auto deserializers = std::make_unique(); - - EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, true}), - sb::cf::ConfigException); -} - -TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullDeserializers) -{ - sb::cf::IValueDeserializersMap::Ptr deserializers; - auto splitter = std::make_unique(); - - EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, true}), - sb::cf::ConfigException); -} - -TEST_F(SettingParserTest, ShouldUseDefaultType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = "value"; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); - EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); -} - -TEST_F(SettingParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--!string=value"; - sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = "value"; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueEmptyKey) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--!string=value"; - sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; - - EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_FALSE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldUseDefaultTypeForUnknownType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!unknown=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor) - .WillOnce(testing::Return(nullptr)) - .WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = "value"; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_FALSE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailDueToForUnknownType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!unknown=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; - - EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailDueToForUnknownDefaultType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!unknown=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); - sb::cf::JsonValue returnedValue = "value"; - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; - - EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_FALSE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} diff --git a/Tests/SettingSplitterTest.cpp b/Tests/SettingSplitterTest.cpp deleted file mode 100644 index 7c3887c..0000000 --- a/Tests/SettingSplitterTest.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include -#include - -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" -#include "Utilities/ParamsTest.hpp" - -class SettingSplitterTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - SettingSplitterTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -static Params SplitSettingData = { - {"--", {{""}}}, - {"one", {{"one"}}}, - {"--====", {{""}, std::nullopt, "==="}}, - {"--option!!type=val", {{"option!"}, "type", "val"}}, - {"--option!type=value", {{"option"}, "type", "value"}}, - {"//option___type;value", {{"option"}, "type", "value"}}, - {"--option=value", {{"option"}, std::nullopt, "value"}}, - {"--option=", {{"option"}, std::nullopt, ""}}, - {"--option!!type=", {{"option!"}, "type", ""}}, - {"--option!!type", {{"option!"}, "type"}}, - {"--option!!=type=val", {{"option!"}, "", "type=val"}}, - {"--option!!:inner!=type=val", {{"option!!", "inner"}, "", "type=val"}}, - {"--option!!:inner:::!=type=val", {{"option!!", "inner", "", "", ""}, "", "type=val"}}, - {":option:inner=type=val", {{"", "option", "inner"}, std::nullopt, "type=val"}}, - {"!!option:inner=type=val", {{"!"}, "option:inner", "type=val"}}, - {":option:inner=value", {{"", "option", "inner"}, std::nullopt, "value"}}, - {":option:inner=value::!", {{"", "option", "inner"}, std::nullopt, "value::!"}}, - {":option!!:inner=value::!", {{"", "option!"}, ":inner", "value::!"}}, - {":::!=value::!", {{"", "", "", ""}, "", "value::!"}}, - {"::!:=value::!", {{"", "", ""}, ":", "value::!"}}, - {":=:!:=value::!", {{"", ""}, std::nullopt, ":!:=value::!"}}, - {"__=:!___=value::!", {{"", ""}, std::nullopt, ":!___=value::!"}}, - {"_______=value", {{"", "", ""}, "", "value"}}, - {"__hello_____type=value", {{"", "hello", ""}, "type", "value"}}, - {"__hello__type=value", {{"", "hello", "type"}, std::nullopt, "value"}}, -}; -PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) -{ - const auto &[setting, expected] = GetParam(); - sb::cf::details::SettingSplitter splitter{{"--", "//"}, {"=", ";"}, {"!", "___"}, {":", "__"}}; - - EXPECT_EQ(splitter.split(setting), expected); -} - -static OneParams SettingSplittersData = {"=", ">", "===", "<<<<", "////"}; - -static OneParams SettingPrefixesData = {":", ";", "%%$", "***", "++"}; - -static OneParams KeySplittersData = {":", ";", "__", "{}", "\\"}; - -static OneParams TypeMarkersData = {"!", "@", ":", "[]", "{}"}; - -static Params, std::string, std::string> SplitSettingSimpleData = { - {{"key1", "key2", "key3"}, "string", "value"}, - {{"key1"}, "string", "value"}, - {{""}, "", ""}, - {{""}, "type", ""}, - {{"key"}, "", ""}, - {{""}, "", "value"}, -}; -PARAMS_TEST_COMBINED_5(SettingSplitterTest, ShouldSplitWithDifferentSplitters, SettingPrefixesData, - SettingSplittersData, TypeMarkersData, KeySplittersData, SplitSettingSimpleData) -{ - const auto &[prefix, settingSplitter, typeMarker, keySplitter, setting] = GetParam(); - const auto &[keys, type, value] = setting; - - sb::cf::details::SettingSplitter splitter{{prefix}, {settingSplitter}, {typeMarker}, {keySplitter}}; - - auto key = sb::cf::details::utils::joinViews(keys, keySplitter); - auto fullSetting = prefix + key + typeMarker + type + settingSplitter + value; - EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); -} - -static Params, std::vector, std::vector, - std::vector, sb::cf::ISettingSplitter::Result> - EmptySplittersData = { - {{}, {"=", ";"}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int", "value"}}, - {{"--", "//"}, {}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, - {{"--", "//"}, {"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, - {{"--", "//"}, {"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, - {{}, {}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int=value"}}, - {{}, {"=", ";"}, {}, {":", "__"}, {{"--key", "deep!int"}, std::nullopt, "value"}}, - {{}, {"=", ";"}, {"!", "___"}, {}, {{"--key:deep"}, "int", "value"}}, - {{"--", "//"}, {}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, - {{"--", "//"}, {}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, - {{"--", "//"}, {"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, - {{}, {}, {}, {":", "__"}, {{"--key", "deep!int=value"}}}, - {{}, {}, {"!", "___"}, {}, {{"--key:deep"}, "int=value"}}, - {{}, {"=", ";"}, {}, {}, {{"--key:deep!int"}, std::nullopt, "value"}}, - {{"--", "//"}, {}, {}, {}, {{"key:deep!int=value"}}}, - {{}, {}, {}, {}, {{"--key:deep!int=value"}}}, - -}; -PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersData) -{ - const auto &[prefixes, settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); - - sb::cf::details::SettingSplitter splitter{prefixes, settingSplitters, typeMarkers, keySplitters}; - - EXPECT_EQ(splitter.split("--key:deep!int=value"), expected); -} diff --git a/Tests/TestTemplate.cpp b/Tests/TestTemplate.cpp deleted file mode 100644 index a6fc38a..0000000 --- a/Tests/TestTemplate.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -class Template : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - Template() {} - - void SetUp() override {} - - void TearDown() override {} - - ~Template() {} - - static void TearDownTestSuite() {} -}; - -TEST_F(Template, ExampleTest) {} diff --git a/Tests/Utilities/ParamsTest.hpp b/Tests/Utilities/ParamsTest.hpp deleted file mode 100644 index 09f0e42..0000000 --- a/Tests/Utilities/ParamsTest.hpp +++ /dev/null @@ -1,53 +0,0 @@ - -#include -#include -#include - -#include "SevenBit/Conf/Json.hpp" - -template using Param = std::tuple; -template using Params = std::vector>; -template using OneParams = std::vector; - -template struct TypeExtractor -{ - using value_type = typename T::iterator::value_type; -}; - -template struct TypeExtractor> -{ - using value_type = typename std::tuple::value_type...>; -}; -#define SETUP_PARAMS_TEST(group, name, values) \ - struct name : public testing::TestWithParam::value_type> \ - { \ - }; \ - INSTANTIATE_TEST_SUITE_P(group, name, values); - -#define PARAMS_TEST(group, name, values) \ - SETUP_PARAMS_TEST(group, name, testing::ValuesIn(values)) \ - TEST_P(name, ) - -#define PARAMS_TEST_COMBINED_1(group, name, one) \ - SETUP_PARAMS_TEST(group, name, testing::Combine(testing::ValuesIn(one))) TEST_P(name, ) - -#define PARAMS_TEST_COMBINED_2(group, name, one, two) \ - SETUP_PARAMS_TEST(group, name, testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two))) \ - TEST_P(name, ) - -#define PARAMS_TEST_COMBINED_3(group, name, one, two, three) \ - SETUP_PARAMS_TEST(group, name, \ - testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two), testing::ValuesIn(three))) \ - TEST_P(name, ) - -#define PARAMS_TEST_COMBINED_4(group, name, one, two, three, four) \ - SETUP_PARAMS_TEST(group, name, \ - testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two), testing::ValuesIn(three), \ - testing::ValuesIn(four))) \ - TEST_P(name, ) - -#define PARAMS_TEST_COMBINED_5(group, name, one, two, three, four, five) \ - SETUP_PARAMS_TEST(group, name, \ - testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two), testing::ValuesIn(three), \ - testing::ValuesIn(four), testing::ValuesIn(five))) \ - TEST_P(name, ) diff --git a/Tests/UtilsTest.cpp b/Tests/UtilsTest.cpp deleted file mode 100644 index 6cfc816..0000000 --- a/Tests/UtilsTest.cpp +++ /dev/null @@ -1,327 +0,0 @@ -#include -#include - -#include "SevenBit/Conf/Details/Utils.hpp" -#include "Utilities/ParamsTest.hpp" - -class UtilsTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - UtilsTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -Params CheckNumberStringsData{ - {"123", true}, {"1", true}, {"0912837545234123", true}, {"asd", false}, - {"", false}, {"alk1", false}, {"1223-", false}, {"1223#", false}, - {"1223+=", false}, {"1223.123", false}, {"1223.123", false}, -}; -PARAMS_TEST(UtilsTest, ShouldCheckNumberStrings, CheckNumberStringsData) -{ - auto &[string, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::isNumberString(string), expected); -} - -Params IgnoreCaseLessData{ - {"", "", false}, - {"I", "i", false}, - {"i", "I", false}, - {"123@#", "123@#", false}, - {"abcdef", "ABCDEF", false}, - {"abcDEF", "abcdef", false}, - {"abcDEF", "ABCdef", false}, - {"abcDEF12##", "abCdef12##", false}, - {"", "abcded", true}, - {"BA", "ab", false}, - {"ab", "ab\n", true}, - {"1222", "12", false}, -}; -PARAMS_TEST(UtilsTest, ShouldIgnoreCaseLessCompareStrings, IgnoreCaseLessData) -{ - auto &[string, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::ignoreCaseLess(string, search), expected); -} - -Params IgnoreCaseEqualsData{ - {"", "", true}, - {"I", "i", true}, - {"i", "I", true}, - {"123@#", "123@#", true}, - {"abcdef", "ABCDEF", true}, - {"abcDEF", "abcdef", true}, - {"abcDEF", "ABCdef", true}, - {"abcDEF12##", "abCdef12##", true}, - {"", "abcded", false}, - {"BA", "ab", false}, - {"ab", "ab\n", false}, - {"1222", "12", false}, -}; -PARAMS_TEST(UtilsTest, ShouldIgnoreCaseCompareStrings, IgnoreCaseEqualsData) -{ - auto &[string, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::ignoreCaseEqual(string, search), expected); -} - -Params ContainsAtData{ - {"", 0, "", false}, {"hello", 0, "", false}, {"", 12, "", false}, - {"i", 0, "i", true}, {"index", 0, "in", true}, {"index", 3, "in", false}, - {"index", 30, "in", false}, {"index", 2, "dex", true}, {"index", 2, "dexx", false}, - {"index", 2, "dexx", false}, {"index", 3, "dex", false}, {"123@#", 0, "123@#", true}, - {"123@#", 2, "3", true}, {"123@#", 2, "@", false}, {"abcdef", 0, "abcdef", true}, - {"", 0, "abcded", false}, {"BA", 0, "ab", false}, {"ab", 0, "ab\n", false}, - {"1222", 0, "12", true}, -}; -PARAMS_TEST(UtilsTest, ShouldContainsAt, ContainsAtData) -{ - auto &[string, index, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::containsAt(string, index, search), expected); -} - -Params, std::optional> ContainsAtMultitData{ - {"", 0, {""}, std::nullopt}, - {"", 12, {""}, std::nullopt}, - {"i", 0, {"i"}, "i"}, - {"index", 0, {"none", "in"}, "in"}, - {"index", 0, {"oh", "bob", "in"}, "in"}, - {"index", 30, {"in"}, std::nullopt}, - {"index", 2, {"yes", "dex"}, "dex"}, - {"index", 2, {"dexx"}, std::nullopt}, - {"index", 2, {"dexx"}, std::nullopt}, - {"index", 3, {"dex"}, std::nullopt}, - {"123@#", 0, {"123@#"}, "123@#"}, - {"123@#", 2, {"3"}, "3"}, - {"123@#", 2, {"@"}, std::nullopt}, - {"abcdef", 0, {"abcdef"}, "abcdef"}, - {"", 0, {"abcded"}, std::nullopt}, - {"BA", 0, {"ab"}, std::nullopt}, - {"ab", 0, {"ab\n"}, std::nullopt}, - {"1222", 0, {"12"}, "12"}, -}; -PARAMS_TEST(UtilsTest, ShouldContainsAtMulti, ContainsAtMultitData) -{ - auto &[string, index, searches, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::containsAt(string, index, searches), expected); -} - -Params ContainsAtFromEndData{ - {"", 0, "", false}, {"", 12, "", false}, {"i", 0, "i", true}, - {"index", 1, "in", true}, {"index", 4, "in", false}, {"index", 30, "in", false}, - {"index", 4, "dex", true}, {"index", 2, "dexx", false}, {"index", 2, "dexx", false}, - {"index", 2, "dex", false}, {"123@#", 4, "123@#", true}, {"123@#", 2, "3", true}, - {"123@#", 2, "@", false}, {"abcdef", 5, "abcdef", true}, {"", 0, "abcded", false}, - {"BA", 2, "ab", false}, {"ab", 2, "ab\n", false}, {"1222", 1, "12", true}, -}; -PARAMS_TEST(UtilsTest, ShouldContainsAtFromEnd, ContainsAtFromEndData) -{ - auto &[string, index, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::containsAtFromEnd(string, index, search), expected); -} - -Params, std::optional> - ContainsAtFromEndMultitData{ - {"", 0, {}, std::nullopt}, - {"hello", 0, {}, std::nullopt}, - {"", 0, {""}, std::nullopt}, - {"", 12, {""}, std::nullopt}, - {"i", 0, {"i"}, "i"}, - {"index", 1, {"none", "in"}, "in"}, - {"index", 1, {"oh", "bob", "in"}, "in"}, - {"index", 30, {"in"}, std::nullopt}, - {"index", 4, {"yes", "dex"}, "dex"}, - {"index", 2, {"dexx"}, std::nullopt}, - {"index", 2, {"dexx"}, std::nullopt}, - {"index", 3, {"dex"}, std::nullopt}, - {"123@#", 4, {"123@#"}, "123@#"}, - {"123@#", 2, {"3"}, "3"}, - {"123@#", 2, {"@"}, std::nullopt}, - {"abcdef", 5, {"abcdef"}, "abcdef"}, - {"", 0, {"abcded"}, std::nullopt}, - {"BA", 0, {"ab"}, std::nullopt}, - {"ab", 0, {"ab\n"}, std::nullopt}, - {"1222", 1, {"12"}, "12"}, - }; -PARAMS_TEST(UtilsTest, ShouldContainsAtFromEndMulti, ContainsAtFromEndMultitData) -{ - auto &[string, index, searches, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::containsAtFromEnd(string, index, searches), expected); -} - -Params StartsWithData{ - {"", "", true}, {"1234567", "123", true}, {"1234567", "", true}, - {"1234567", "1", true}, {"123", "123", true}, {"123", "1234", false}, - {"123", "890", false}, {"123", "1245", false}, {"1234567", "234", false}, - {"", "234", false}, {"123", "234", false}, -}; -PARAMS_TEST(UtilsTest, ShouldStartsWith, StartsWithData) -{ - auto &[string, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::startsWith(string, search), expected); -} - -Params> SplitStrData{ - {"", "/", {""}}, - {"", "", {""}}, - {"abcd", "", {"abcd"}}, - {"first:sec:for:5:6", "/", {"first:sec:for:5:6"}}, - {"first:sec:for:5:6", ":", {"first", "sec", "for", "5", "6"}}, - {"first123++/?sec123++/?for123++/?5123++/?6", "123++/?", {"first", "sec", "for", "5", "6"}}, - {":first:sec:for:5:6", ":", {"", "first", "sec", "for", "5", "6"}}, - {"first::sec:for:5:6", ":", {"first", "", "sec", "for", "5", "6"}}, - {"first:sec:for:5:6:", ":", {"first", "sec", "for", "5", "6", ""}}, - {"first__sec__for__5__6__", "__", {"first", "sec", "for", "5", "6", ""}}, - {":::", ":", {"", "", "", ""}}, -}; -PARAMS_TEST(UtilsTest, ShouldSplitString, SplitStrData) -{ - auto &[string, delim, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::split(string, delim), expected); -} - -Params, std::vector> SplitStrMultiData{ - {"", {}, {""}}, - {"hello", {}, {"hello"}}, - {"", {"/", "|"}, {""}}, - {"", {"", "|"}, {""}}, - {"abcd", {""}, {"abcd"}}, - {"ab:cd", {"", ":"}, {"ab", "cd"}}, - {"first:sec__for:5||6", {":", "__", "||"}, {"first", "sec", "for", "5", "6"}}, - {"first:sec__for:5||6", {":", "||"}, {"first", "sec__for", "5", "6"}}, - {"first:sec__for:5||6", {"???"}, {"first:sec__for:5||6"}}, - {"first:sec:for:5:6", {":"}, {"first", "sec", "for", "5", "6"}}, - {"first123++/?sec123++/?for123++/?5123++/?6", {"123++/?"}, {"first", "sec", "for", "5", "6"}}, - {":first:sec:for:5:6", {":"}, {"", "first", "sec", "for", "5", "6"}}, - {"first::sec:for:5:6", {":"}, {"first", "", "sec", "for", "5", "6"}}, - {"first:sec:for:5:6:", {":"}, {"first", "sec", "for", "5", "6", ""}}, - {":::", {":"}, {"", "", "", ""}}, -}; -PARAMS_TEST(UtilsTest, ShouldSplitStringMulti, SplitStrMultiData) -{ - auto &[string, delims, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::split(string, delims), expected); -} - -Params, std::optional>> - BreakStrData{ - {"", {""}, std::nullopt}, - {"", {}, std::nullopt}, - {"hello", {}, std::nullopt}, - {"key=value", {"="}, std::pair{"key", "value"}}, - {"key====value", {"===="}, std::pair{"key", "value"}}, - {"=keyvalue", {"="}, std::pair{"", "keyvalue"}}, - {"keyvalue=", {"="}, std::pair{"keyvalue", ""}}, - {"key=value", {"-"}, std::nullopt}, - {"abcd", {""}, std::nullopt}, - {"first=sec:for:5:6", {"/"}, std::nullopt}, - {"first:sec:for:5:6", {":"}, std::pair{"first", "sec:for:5:6"}}, - {"first__sec__for__5__6__", {"__"}, std::pair{"first", "sec__for__5__6__"}}, - }; -PARAMS_TEST(UtilsTest, ShouldBreakString, BreakStrData) -{ - auto &[string, delim, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::tryBreak(string, delim), expected); -} - -Params, std::optional>> - BreakFromEndStrData{ - {"", {""}, std::nullopt}, - {"", {}, std::nullopt}, - {"hello", {}, std::nullopt}, - {"key=value", {"="}, std::pair{"key", "value"}}, - {"key====value", {"===="}, std::pair{"key", "value"}}, - {"=keyvalue", {"="}, std::pair{"", "keyvalue"}}, - {"keyvalue=", {"="}, std::pair{"keyvalue", ""}}, - {"key=value", {"-"}, std::nullopt}, - {"abcd", {""}, std::nullopt}, - {"first=sec:for:5:6", {"/"}, std::nullopt}, - {"first:sec:for:5:6", {":"}, std::pair{"first:sec:for:5", "6"}}, - {"first__sec__for__5__6", {"__"}, std::pair{"first__sec__for__5", "6"}}, - }; -PARAMS_TEST(UtilsTest, ShouldBreakFromEndString, BreakFromEndStrData) -{ - auto &[string, delim, expected] = GetParam(); - auto res = sb::cf::details::utils::tryBreakFromEnd(string, delim); - EXPECT_EQ(sb::cf::details::utils::tryBreakFromEnd(string, delim), expected); -} - -Params, std::string, std::string> JoinStrData{ - {{""}, "/", ""}, - {{"first:sec:for:5:6"}, "/", "first:sec:for:5:6"}, - {{"first", "sec", "for", "5", "6"}, ":", "first:sec:for:5:6"}, - {{"first", "sec", "for", "5", "6"}, "123++/?", "first123++/?sec123++/?for123++/?5123++/?6"}, - {{"", "first", "sec", "for", "5", "6"}, ":", ":first:sec:for:5:6"}, - {{"first", "", "sec", "for", "5", "6"}, ":", "first::sec:for:5:6"}, - {{"first", "sec", "for", "5", "6", ""}, ":", "first:sec:for:5:6:"}, - {{"first", "sec:for:5:6:"}, ":", "first:sec:for:5:6:"}, - {{"first", "sec", "for:5:6:"}, ":", "first:sec:for:5:6:"}, - {{"first", "sec", "for", "5", "6"}, ":", "first:sec:for:5:6"}, -}; -PARAMS_TEST(UtilsTest, ShouldJoinStrings, JoinStrData) -{ - auto &[strings, delim, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::joinViews(strings, delim), expected); -} - -TEST_F(UtilsTest, ShouldAssertPtr) -{ - int i = 1; - EXPECT_NO_THROW(sb::cf::details::utils::assertPtr(&i)); - EXPECT_THROW(sb::cf::details::utils::assertPtr(nullptr), sb::cf::NullPointerException); -} - -Params> ConvertToNumberIntData{ - {"123", true, {true, 123}}, {"-123", true, {true, -123}}, {"00123", true, {true, 123}}, - {"123 23", false, {true, 123}}, {"123.23", false, {true, 123}}, {"123adawadwa", false, {true, 123}}, -}; -PARAMS_TEST(UtilsTest, ShouldConvertToIntNumber, ConvertToNumberIntData) -{ - auto &[string, full, expected] = GetParam(); - auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); - - EXPECT_EQ(success, expected.first); - if (success) - { - EXPECT_EQ(result, expected.second); - } -} - -Params> ConvertToNumberDoubleData{ - {"123.123", true, {true, 123.123}}, {" 123.123", true, {true, 123.123}}, {"-123.22", true, {true, -123.22}}, - {"00123.2", true, {true, 123.2}}, {" 123.23asdw", false, {true, 123.23}}, {"123.23", false, {true, 123.23}}, - {"123.1adawadwa", false, {true, 123.1}}, -}; -PARAMS_TEST(UtilsTest, ShouldConvertToDoubleNumber, ConvertToNumberDoubleData) -{ - auto &[string, full, expected] = GetParam(); - auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); - - EXPECT_EQ(success, expected.first); - if (success) - { - EXPECT_EQ(result, expected.second); - } -} - -Params> ConvertToBoolData{ - {"true", true, {true, true}}, {"false", true, {true, false}}, {"-123.22", true, {false, false}}, - {"00123.2", true, {false, false}}, {"0", true, {true, false}}, {"0", true, {true, false}}, - {"0 asdad", false, {true, false}}, {"12 asdad", false, {true, true}}, {" 12", true, {true, true}}, - {"ttrue", true, {false, true}}, -}; -PARAMS_TEST(UtilsTest, ShouldConvertToBool, ConvertToBoolData) -{ - auto &[string, full, expected] = GetParam(); - auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); - - EXPECT_EQ(success, expected.first); - if (success) - { - EXPECT_EQ(result, expected.second); - } -} From 0a08b2173348b424b7d762d1d8b3e702539b8647 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Mon, 19 Feb 2024 18:06:42 +0100 Subject: [PATCH 03/31] refactor, remove conan lib provider --- CMakeLists.txt | 52 +-- Cmake/Setup.cmake | 80 +++-- Include/SevenBit/Conf.hpp | 1 - Include/SevenBit/Conf/CmakeDef.hpp | 19 +- Include/SevenBit/Conf/CmakeDef.hpp.input | 17 - .../SevenBit/Conf/ConfigurationBuilder.hpp | 2 +- .../SevenBit/Conf/ConfigurationManager.hpp | 19 - .../Conf/{ => Details}/Configuration.hpp | 0 .../Conf/Details/Impl/Configuration.hpp | 2 +- .../Details/Impl/ConfigurationManager.hpp | 18 - Include/SevenBit/Conf/Exceptions.hpp | 2 +- .../SevenBit/Conf/IConfigurationBuilder.hpp | 17 +- .../Impl/ConfigurationBuilder.hpp | 3 +- .../Conf/{Details => }/Impl/Exceptions.hpp | 0 .../Impl/SettingParserBuilder.hpp | 0 Include/SevenBit/Conf/LibraryConfig.hpp | 42 ++- .../SevenBit/Conf/SettingParserBuilder.hpp | 2 +- .../AppSettingsConfiguration.hpp | 2 +- .../{ => Sources}/ChainedConfiguration.hpp | 4 +- .../CommandLineConfiguration.hpp | 4 +- .../ConfigurationProviderBase.hpp | 2 +- .../EnvironmentVarsConfiguration.hpp | 4 +- .../Impl/AppSettingsConfiguration.hpp | 7 +- .../Impl/ChainedConfiguration.hpp | 3 +- .../Impl/CommandLineConfiguration.hpp | 3 +- .../Impl/ConfigurationProviderBase.hpp | 3 +- .../Impl/EnvironmentVarsConfiguration.hpp | 3 +- .../Impl/InMemoryConfiguration.hpp | 2 +- .../Impl/JsonConfiguration.hpp | 2 +- .../Impl/JsonFileConfiguration.hpp | 2 +- .../Impl/JsonStreamConfiguration.hpp | 2 +- .../Impl/KeyPerFileConfiguration.hpp | 8 +- .../Impl/MapConfiguration.hpp | 2 +- .../{ => Sources}/InMemoryConfiguration.hpp | 4 +- .../Conf/{ => Sources}/JsonConfiguration.hpp | 4 +- .../{ => Sources}/JsonFileConfiguration.hpp | 4 +- .../{ => Sources}/JsonStreamConfiguration.hpp | 4 +- .../{ => Sources}/KeyPerFileConfiguration.hpp | 4 +- .../Conf/{ => Sources}/MapConfiguration.hpp | 4 +- Source/CMakeLists.txt | 44 +-- Source/Source.cpp | 29 +- Tests/CMakeLists.txt | 28 +- Tests/EndToEnd/CMakeLists.txt | 7 + Tests/EndToEnd/Examples/expectedTestData.json | 14 + Tests/EndToEnd/Examples/test_examples.py | 69 ++++ Tests/Helpers/Classes/CustomConfigSource.hpp | 18 + Tests/Helpers/Files/Directory/settingOne.json | 9 + Tests/Helpers/Files/Directory/settingTwo.json | 10 + Tests/Helpers/Files/appsettings.dev.json | 10 + Tests/Helpers/Files/appsettings.json | 9 + Tests/Helpers/Files/bad.json | 1 + .../Mocks/ConfigurationBuilderMock.hpp | 16 + Tests/Helpers/Mocks/DeserializerMock.hpp | 12 + Tests/Helpers/Mocks/SettingSplitterMock.hpp | 11 + .../Mocks/ValueDeserializersMapMock.hpp | 10 + Tests/Helpers/Utilities/ParamsTest.hpp | 53 +++ Tests/Integration/CMakeLists.txt | 15 + Tests/Integration/RunTests.cpp | 10 + Tests/Integration/TestTemplate.cpp | 19 + Tests/Unit/AppSettingsConfigurationTest.cpp | 48 +++ Tests/Unit/CMakeLists.txt | 24 ++ Tests/Unit/ChainedConfigurationTest.cpp | 80 +++++ Tests/Unit/CommandLineConfigurationTest.cpp | 93 +++++ Tests/Unit/ConfigurationBuilderTest.cpp | 91 +++++ Tests/Unit/ConfigurationTest.cpp | 94 +++++ Tests/Unit/DeserializersTest.cpp | 237 +++++++++++++ .../Unit/EnvironmentVarsConfigurationTest.cpp | 98 ++++++ Tests/Unit/InMemoryConfigurationTest.cpp | 39 +++ Tests/Unit/JsonConfigurationTest.cpp | 37 ++ Tests/Unit/JsonFileConfigurationTest.cpp | 62 ++++ Tests/Unit/JsonObjectExtTest.cpp | 281 +++++++++++++++ Tests/Unit/JsonStreamConfigurationTest.cpp | 66 ++++ Tests/Unit/KeyPerFileConfigurationTest.cpp | 87 +++++ Tests/Unit/MapConfigutationTest.cpp | 44 +++ Tests/Unit/RunTests.cpp | 10 + Tests/Unit/SettingParserBuilderTest.cpp | 108 ++++++ Tests/Unit/SettingParserTest.cpp | 182 ++++++++++ Tests/Unit/SettingSplitterTest.cpp | 114 ++++++ Tests/Unit/TestTemplate.cpp | 20 ++ Tests/Unit/UtilsTest.cpp | 327 ++++++++++++++++++ conanfile.txt | 9 - 81 files changed, 2654 insertions(+), 244 deletions(-) delete mode 100644 Include/SevenBit/Conf/ConfigurationManager.hpp rename Include/SevenBit/Conf/{ => Details}/Configuration.hpp (100%) delete mode 100644 Include/SevenBit/Conf/Details/Impl/ConfigurationManager.hpp rename Include/SevenBit/Conf/{Details => }/Impl/ConfigurationBuilder.hpp (96%) rename Include/SevenBit/Conf/{Details => }/Impl/Exceptions.hpp (100%) rename Include/SevenBit/Conf/{Details => }/Impl/SettingParserBuilder.hpp (100%) rename Include/SevenBit/Conf/{ => Sources}/AppSettingsConfiguration.hpp (92%) rename Include/SevenBit/Conf/{ => Sources}/ChainedConfiguration.hpp (92%) rename Include/SevenBit/Conf/{ => Sources}/CommandLineConfiguration.hpp (93%) rename Include/SevenBit/Conf/{ => Sources}/ConfigurationProviderBase.hpp (92%) rename Include/SevenBit/Conf/{ => Sources}/EnvironmentVarsConfiguration.hpp (92%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/AppSettingsConfiguration.hpp (84%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/ChainedConfiguration.hpp (96%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/CommandLineConfiguration.hpp (97%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/ConfigurationProviderBase.hpp (93%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/EnvironmentVarsConfiguration.hpp (97%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/InMemoryConfiguration.hpp (95%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/JsonConfiguration.hpp (94%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/JsonFileConfiguration.hpp (97%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/JsonStreamConfiguration.hpp (95%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/KeyPerFileConfiguration.hpp (94%) rename Include/SevenBit/Conf/{Details => Sources}/Impl/MapConfiguration.hpp (97%) rename Include/SevenBit/Conf/{ => Sources}/InMemoryConfiguration.hpp (91%) rename Include/SevenBit/Conf/{ => Sources}/JsonConfiguration.hpp (90%) rename Include/SevenBit/Conf/{ => Sources}/JsonFileConfiguration.hpp (91%) rename Include/SevenBit/Conf/{ => Sources}/JsonStreamConfiguration.hpp (90%) rename Include/SevenBit/Conf/{ => Sources}/KeyPerFileConfiguration.hpp (93%) rename Include/SevenBit/Conf/{ => Sources}/MapConfiguration.hpp (92%) create mode 100644 Tests/EndToEnd/CMakeLists.txt create mode 100644 Tests/EndToEnd/Examples/expectedTestData.json create mode 100644 Tests/EndToEnd/Examples/test_examples.py create mode 100644 Tests/Helpers/Classes/CustomConfigSource.hpp create mode 100644 Tests/Helpers/Files/Directory/settingOne.json create mode 100644 Tests/Helpers/Files/Directory/settingTwo.json create mode 100644 Tests/Helpers/Files/appsettings.dev.json create mode 100644 Tests/Helpers/Files/appsettings.json create mode 100644 Tests/Helpers/Files/bad.json create mode 100644 Tests/Helpers/Mocks/ConfigurationBuilderMock.hpp create mode 100644 Tests/Helpers/Mocks/DeserializerMock.hpp create mode 100644 Tests/Helpers/Mocks/SettingSplitterMock.hpp create mode 100644 Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp create mode 100644 Tests/Helpers/Utilities/ParamsTest.hpp create mode 100644 Tests/Integration/CMakeLists.txt create mode 100644 Tests/Integration/RunTests.cpp create mode 100644 Tests/Integration/TestTemplate.cpp create mode 100644 Tests/Unit/AppSettingsConfigurationTest.cpp create mode 100644 Tests/Unit/CMakeLists.txt create mode 100644 Tests/Unit/ChainedConfigurationTest.cpp create mode 100644 Tests/Unit/CommandLineConfigurationTest.cpp create mode 100644 Tests/Unit/ConfigurationBuilderTest.cpp create mode 100644 Tests/Unit/ConfigurationTest.cpp create mode 100644 Tests/Unit/DeserializersTest.cpp create mode 100644 Tests/Unit/EnvironmentVarsConfigurationTest.cpp create mode 100644 Tests/Unit/InMemoryConfigurationTest.cpp create mode 100644 Tests/Unit/JsonConfigurationTest.cpp create mode 100644 Tests/Unit/JsonFileConfigurationTest.cpp create mode 100644 Tests/Unit/JsonObjectExtTest.cpp create mode 100644 Tests/Unit/JsonStreamConfigurationTest.cpp create mode 100644 Tests/Unit/KeyPerFileConfigurationTest.cpp create mode 100644 Tests/Unit/MapConfigutationTest.cpp create mode 100644 Tests/Unit/RunTests.cpp create mode 100644 Tests/Unit/SettingParserBuilderTest.cpp create mode 100644 Tests/Unit/SettingParserTest.cpp create mode 100644 Tests/Unit/SettingSplitterTest.cpp create mode 100644 Tests/Unit/TestTemplate.cpp create mode 100644 Tests/Unit/UtilsTest.cpp delete mode 100644 conanfile.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 277ecfe..8aa08eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.5.0) +cmake_minimum_required(VERSION 3.15.0) set(_7BIT_CONF_LIBRARY 7bitConf) set(_7BIT_CONF_VERSION_MAJOR 1) -set(_7BIT_CONF_VERSION_MINOR 1) +set(_7BIT_CONF_VERSION_MINOR 2) set(_7BIT_CONF_VERSION_PATCH 1) set(_7BIT_CONF_VERSION ${_7BIT_CONF_VERSION_MAJOR}.${_7BIT_CONF_VERSION_MINOR}.${_7BIT_CONF_VERSION_PATCH}) @@ -19,28 +19,28 @@ endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) include(Setup) +include(CMakeDependentOption) include(GNUInstallDirs) include_directories(${_7BIT_CONF_INCLUDE_DIR}) add_subdirectory(Source) -if (_7BIT_CONF_BUILD_TESTS) - include(GoogleTest) +if(_7BIT_CONF_BUILD_UNIT_TESTS OR _7BIT_CONF_BUILD_INTEGRATION_TESTS OR _7BIT_CONF_BUILD_E2E_TESTS) enable_testing() add_subdirectory(Tests) -endif () +endif() -if (_7BIT_CONF_BUILD_EXAMPLES) +if(_7BIT_CONF_BUILD_EXAMPLES) add_subdirectory(Examples) -endif () +endif() -if (_7BIT_CONF_BUILD_SINGLE_HEADER) +if(_7BIT_CONF_BUILD_SINGLE_HEADER) add_subdirectory(SingleHeader) -endif () +endif() -if (_7BIT_CONF_INSTALL) +if(_7BIT_CONF_INSTALL) set(PROJECT_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/Cmake/7bitConfConfig.cmake.in) set(PROJECT_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/7bitConfConfig.cmake) set(CONFIG_TARGETS_FILE 7bitConfConfigTargets.cmake) @@ -50,29 +50,29 @@ if (_7BIT_CONF_INSTALL) install(DIRECTORY ${_7BIT_CONF_INCLUDE_DIR}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( - TARGETS 7bitConf - EXPORT 7bitConf - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + TARGETS 7bitConf + EXPORT 7bitConf + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) export( - TARGETS 7bitConf - NAMESPACE 7bitConf:: - FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_TARGETS_FILE}) + TARGETS 7bitConf + NAMESPACE 7bitConf:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_TARGETS_FILE}) install( - EXPORT 7bitConf - EXPORT 7bitConf - DESTINATION ${EXPORT_DEST_DIR} - NAMESPACE 7bitConf:: - NAMESPACE 7bitConf:: - FILE ${CONFIG_TARGETS_FILE}) + EXPORT 7bitConf + EXPORT 7bitConf + DESTINATION ${EXPORT_DEST_DIR} + NAMESPACE 7bitConf:: + NAMESPACE 7bitConf:: + FILE ${CONFIG_TARGETS_FILE}) include(CMakePackageConfigHelpers) configure_package_config_file(${PROJECT_CONFIG_IN} ${PROJECT_CONFIG_OUT} - INSTALL_DESTINATION ${EXPORT_DEST_DIR}) + INSTALL_DESTINATION ${EXPORT_DEST_DIR}) write_basic_package_version_file(${VERSIONS_CONFIG_FILE} COMPATIBILITY SameMajorVersion) @@ -81,4 +81,4 @@ if (_7BIT_CONF_INSTALL) export(PACKAGE 7bitConf) include(CPack) -endif () +endif() diff --git a/Cmake/Setup.cmake b/Cmake/Setup.cmake index 7a65552..c3a0ff6 100644 --- a/Cmake/Setup.cmake +++ b/Cmake/Setup.cmake @@ -1,8 +1,8 @@ include(Functions) -if (NOT CMAKE_BUILD_TYPE) +if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) -endif () +endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) @@ -28,16 +28,21 @@ set(CPACK_SOURCE_GENERATOR "TGZ;ZIP") set(_7BIT_CONF_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") set(_7BIT_CONF_CONF_DIR "${_7BIT_CONF_INCLUDE_DIR}/SevenBit/Conf") +set(_7BIT_CONF_SOURCES_DIR "${_7BIT_CONF_CONF_DIR}/Sources") +set(_7BIT_CONF_DETAILS_DIR "${_7BIT_CONF_CONF_DIR}/Details") set(_7BIT_CONF_MAIN_HEADER "${_7BIT_CONF_INCLUDE_DIR}/SevenBit/Conf.hpp") -file(GLOB _7BIT_CONF_TOP_HEADERS "${_7BIT_CONF_CONF_DIR}/*.hpp") -file(GLOB _7BIT_CONF_DETAILS_HEADERS "${_7BIT_CONF_CONF_DIR}/Details/*.hpp") -file(GLOB _7BIT_CONF_IMPL_HEADERS "${_7BIT_CONF_CONF_DIR}/Impl/*.hpp") -set(_7BIT_CONF_ALL_HEADERS ${_7BIT_CONF_MAIN_HEADER} ${_7BIT_CONF_TOP_HEADERS} ${_7BIT_CONF_DETAILS_HEADERS} ${_7BIT_CONF_IMPL_HEADERS}) - -source_group("Header Files\\SevenBit" FILES ${_7BIT_CONF_TOP_HEADERS}) -source_group("Header Files\\SevenBit\\Details" FILES ${_7BIT_CONF_DETAILS_HEADERS}) -source_group("Header Files\\SevenBit\\Details\\Impl" FILES ${_7BIT_CONF_IMPL_HEADERS}) +file(GLOB _7BIT_CONF_PUBLIC_HEADERS + "${_7BIT_CONF_CONF_DIR}/*.hpp" + "${_7BIT_CONF_SOURCES_DIR}/*.hpp" +) +file(GLOB _7BIT_CONF_DETAILS_HEADERS "${_7BIT_CONF_DETAILS_DIR}/*.hpp") +file(GLOB _7BIT_CONF_IMPL_HEADERS + "${_7BIT_CONF_CONF_DIR}/Impl/*.hpp" + "${_7BIT_CONF_SOURCES_DIR}/Impl/*.hpp" + "${_7BIT_CONF_DETAILS_DIR}/Impl/*.hpp" +) +set(_7BIT_CONF_ALL_HEADERS ${_7BIT_CONF_MAIN_HEADER} ${_7BIT_CONF_PUBLIC_HEADERS} ${_7BIT_CONF_DETAILS_HEADERS} ${_7BIT_CONF_IMPL_HEADERS}) set(_7BIT_CONF_LIBRARY_TYPE "Static" CACHE STRING "Library build type: Shared;Static;HeaderOnly") set(_7BIT_CONF_LIBRARY_TYPE_VALUES "Shared;Static;HeaderOnly" CACHE STRING "List of possible _7BIT_CONF_LIBRARY_TYPE values") @@ -46,24 +51,33 @@ set_property(CACHE _7BIT_CONF_LIBRARY_TYPE PROPERTY STRINGS Shared Static Header option(_7BIT_CONF_BUILD_PIC "Build position independent code (-fPIC)" OFF) option(_7BIT_CONF_BUILD_EXAMPLES "Build example" OFF) -option(_7BIT_CONF_BUILD_TESTS "Build tests" OFF) +option(_7BIT_CONF_BUILD_ALL_TESTS "Build tests" OFF) +option(_7BIT_CONF_BUILD_UNIT_TESTS "Build unit tests" OFF) +option(_7BIT_CONF_BUILD_INTEGRATION_TESTS "Build integration tests" OFF) +option(_7BIT_CONF_BUILD_E2E_TESTS "Build e2e tests" OFF) option(_7BIT_CONF_INSTALL "Installs 7bitConf" OFF) option(_7BIT_CONF_BUILD_SINGLE_HEADER "Builds single header SevenBitConf.hpp" OFF) -if (_7BIT_CONF_BUILD_PIC) +if(_7BIT_CONF_BUILD_ALL_TESTS) + set(_7BIT_CONF_BUILD_UNIT_TESTS ${_7BIT_CONF_BUILD_ALL_TESTS}) + set(_7BIT_CONF_BUILD_INTEGRATION_TESTS ${_7BIT_CONF_BUILD_ALL_TESTS}) + set(_7BIT_CONF_BUILD_E2E_TESTS ${_7BIT_CONF_BUILD_ALL_TESTS}) +endif() + +if(_7BIT_CONF_BUILD_PIC) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -endif () +endif() -if (_7BIT_CONF_LIBRARY_TYPE STREQUAL "Shared") +if(_7BIT_CONF_LIBRARY_TYPE STREQUAL "Shared") set(_7BIT_CONF_BUILD_LIBRARY_TYPE "Shared") set(_7BIT_CONF_SHARED_LIB true) -elseif (_7BIT_CONF_LIBRARY_TYPE STREQUAL "HeaderOnly") +elseif(_7BIT_CONF_LIBRARY_TYPE STREQUAL "HeaderOnly") set(_7BIT_CONF_BUILD_LIBRARY_TYPE "HeaderOnly") set(_7BIT_CONF_HEADER_ONLY_LIB true) -else () +else() set(_7BIT_CONF_BUILD_LIBRARY_TYPE "Static") set(_7BIT_CONF_STATIC_LIB true) -endif () +endif() configure_file(${_7BIT_CONF_CONF_DIR}/CmakeDef.hpp.input ${_7BIT_CONF_CONF_DIR}/CmakeDef.hpp) @@ -71,20 +85,22 @@ set(BYTE_SIZE 8) math(EXPR MEMORY_SIZE "${CMAKE_SIZEOF_VOID_P} * ${BYTE_SIZE}") set(INFOS - "${_7BIT_CONF_LIBRARY} ${_7BIT_CONF_VERSION}" - "Build type: ${CMAKE_BUILD_TYPE}" - "Library type: ${_7BIT_CONF_BUILD_LIBRARY_TYPE}" - "==================================================" - "Cmake version: ${CMAKE_VERSION}" - "Os: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}" - "Architecture: ${CMAKE_SYSTEM_PROCESSOR} ${MEMORY_SIZE}bit" - "CXX compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" - "CXX standard: ${CMAKE_CXX_STANDARD}" - "Generator: ${CMAKE_GENERATOR}" - "==================================================" - "Build tests: ${_7BIT_CONF_BUILD_TESTS}" - "Build examples: ${_7BIT_CONF_BUILD_EXAMPLES}" - "Build single header: ${_7BIT_CONF_BUILD_SINGLE_HEADER}" - "Install project: ${_7BIT_CONF_INSTALL}" + "${_7BIT_CONF_LIBRARY} ${_7BIT_CONF_VERSION}" + "Build type: ${CMAKE_BUILD_TYPE}" + "Library type: ${_7BIT_CONF_BUILD_LIBRARY_TYPE}" + "==================================================" + "Cmake version: ${CMAKE_VERSION}" + "Os: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}" + "Architecture: ${CMAKE_SYSTEM_PROCESSOR} ${MEMORY_SIZE}bit" + "CXX compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" + "CXX standard: ${CMAKE_CXX_STANDARD}" + "Generator: ${CMAKE_GENERATOR}" + "==================================================" + "Build unit tests: ${_7BIT_CONF_BUILD_UNIT_TESTS}" + "Build integration tests: ${_7BIT_CONF_BUILD_INTEGRATION_TESTS}" + "Build e2e tests: ${_7BIT_CONF_BUILD_E2E_TESTS}" + "Build examples: ${_7BIT_CONF_BUILD_EXAMPLES}" + "Build single header: ${_7BIT_CONF_BUILD_SINGLE_HEADER}" + "Install project: ${_7BIT_CONF_INSTALL}" ) printInfo("${INFOS}" = 50 7 0) diff --git a/Include/SevenBit/Conf.hpp b/Include/SevenBit/Conf.hpp index 45d0f71..1d5d549 100644 --- a/Include/SevenBit/Conf.hpp +++ b/Include/SevenBit/Conf.hpp @@ -3,6 +3,5 @@ #include "SevenBit/Conf/LibraryConfig.hpp" #include "SevenBit/Conf/ConfigurationBuilder.hpp" -#include "SevenBit/Conf/ConfigurationManager.hpp" #include "SevenBit/Conf/Exceptions.hpp" #include "SevenBit/Conf/ObjectHolder.hpp" diff --git a/Include/SevenBit/Conf/CmakeDef.hpp b/Include/SevenBit/Conf/CmakeDef.hpp index 077e658..a3b0b8d 100644 --- a/Include/SevenBit/Conf/CmakeDef.hpp +++ b/Include/SevenBit/Conf/CmakeDef.hpp @@ -13,22 +13,5 @@ #endif #define _7BIT_CONF_VERSION_MAJOR 1 -#define _7BIT_CONF_VERSION_MINOR 1 +#define _7BIT_CONF_VERSION_MINOR 2 #define _7BIT_CONF_VERSION_PATCH 1 - -#define _7BIT_CONF_VERSION "1.1.1" - -#ifndef _7BIT_CONF_VERSION_MAJOR -#define _7BIT_CONF_VERSION_MAJOR 0 -#endif - -#ifndef _7BIT_CONF_VERSION_MINOR -#define _7BIT_CONF_VERSION_MINOR 0 -#endif - -#ifndef _7BIT_CONF_VERSION_PATCH -#define _7BIT_CONF_VERSION_PATCH 0 -#endif - -#define _7BIT_CONF_VERSION_AS_NUMBER \ - (_7BIT_CONF_VERSION_MAJOR * 10000 + _7BIT_CONF_VERSION_MINOR * 100 + _7BIT_CONF_VERSION_PATCH) diff --git a/Include/SevenBit/Conf/CmakeDef.hpp.input b/Include/SevenBit/Conf/CmakeDef.hpp.input index 0a33fcc..c75c51a 100644 --- a/Include/SevenBit/Conf/CmakeDef.hpp.input +++ b/Include/SevenBit/Conf/CmakeDef.hpp.input @@ -15,20 +15,3 @@ #cmakedefine _7BIT_CONF_VERSION_MAJOR @_7BIT_CONF_VERSION_MAJOR@ #cmakedefine _7BIT_CONF_VERSION_MINOR @_7BIT_CONF_VERSION_MINOR@ #cmakedefine _7BIT_CONF_VERSION_PATCH @_7BIT_CONF_VERSION_PATCH@ - -#cmakedefine _7BIT_CONF_VERSION "@_7BIT_CONF_VERSION@" - -#ifndef _7BIT_CONF_VERSION_MAJOR -#define _7BIT_CONF_VERSION_MAJOR 0 -#endif - -#ifndef _7BIT_CONF_VERSION_MINOR -#define _7BIT_CONF_VERSION_MINOR 0 -#endif - -#ifndef _7BIT_CONF_VERSION_PATCH -#define _7BIT_CONF_VERSION_PATCH 0 -#endif - -#define _7BIT_CONF_VERSION_AS_NUMBER \ - (_7BIT_CONF_VERSION_MAJOR * 10000 + _7BIT_CONF_VERSION_MINOR * 100 + _7BIT_CONF_VERSION_PATCH) diff --git a/Include/SevenBit/Conf/ConfigurationBuilder.hpp b/Include/SevenBit/Conf/ConfigurationBuilder.hpp index 6cbb20e..8a43d6a 100644 --- a/Include/SevenBit/Conf/ConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/ConfigurationBuilder.hpp @@ -45,5 +45,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/ConfigurationBuilder.hpp" +#include "SevenBit/Conf/Impl/ConfigurationBuilder.hpp" #endif diff --git a/Include/SevenBit/Conf/ConfigurationManager.hpp b/Include/SevenBit/Conf/ConfigurationManager.hpp deleted file mode 100644 index b869687..0000000 --- a/Include/SevenBit/Conf/ConfigurationManager.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "SevenBit/Conf/LibraryConfig.hpp" - -#include "SevenBit/Conf/Configuration.hpp" -#include "SevenBit/Conf/ConfigurationBuilder.hpp" - -namespace sb::cf -{ - class EXPORT ConfigurationManager : public ConfigurationBuilder, public Configuration - { - public: - IConfigurationBuilder &add(IConfigurationSource::SPtr source) override; - }; -} // namespace sb::cf - -#ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/ConfigurationManager.hpp" -#endif diff --git a/Include/SevenBit/Conf/Configuration.hpp b/Include/SevenBit/Conf/Details/Configuration.hpp similarity index 100% rename from Include/SevenBit/Conf/Configuration.hpp rename to Include/SevenBit/Conf/Details/Configuration.hpp diff --git a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp index 26795e8..7b627b8 100644 --- a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp @@ -3,7 +3,7 @@ #include #include -#include "SevenBit/Conf/Configuration.hpp" +#include "SevenBit/Conf/Details/Configuration.hpp" #include "SevenBit/Conf/Details/JsonExt.hpp" #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Exceptions.hpp" diff --git a/Include/SevenBit/Conf/Details/Impl/ConfigurationManager.hpp b/Include/SevenBit/Conf/Details/Impl/ConfigurationManager.hpp deleted file mode 100644 index 0efdc65..0000000 --- a/Include/SevenBit/Conf/Details/Impl/ConfigurationManager.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "SevenBit/Conf/ConfigurationManager.hpp" -#include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" - -namespace sb::cf -{ - INLINE IConfigurationBuilder &ConfigurationManager::add(IConfigurationSource::SPtr source) - { - ConfigurationBuilder::add(source); - auto &provider = getProviders().emplace_back(source->build(*this)); - details::utils::assertPtr(provider); - provider->load(); - details::JsonExt::deepMerge(rootAsObject(), std::move(provider->getConfiguration())); - return *this; - } -} // namespace sb::cf diff --git a/Include/SevenBit/Conf/Exceptions.hpp b/Include/SevenBit/Conf/Exceptions.hpp index 2e3359e..ad105b2 100644 --- a/Include/SevenBit/Conf/Exceptions.hpp +++ b/Include/SevenBit/Conf/Exceptions.hpp @@ -49,5 +49,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/Exceptions.hpp" +#include "SevenBit/Conf/Impl/Exceptions.hpp" #endif diff --git a/Include/SevenBit/Conf/IConfigurationBuilder.hpp b/Include/SevenBit/Conf/IConfigurationBuilder.hpp index 8e8c5dd..1e682de 100644 --- a/Include/SevenBit/Conf/IConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/IConfigurationBuilder.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -12,18 +11,18 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/AppSettingsConfiguration.hpp" -#include "SevenBit/Conf/CommandLineConfiguration.hpp" -#include "SevenBit/Conf/EnvironmentVarsConfiguration.hpp" #include "SevenBit/Conf/IConfiguration.hpp" #include "SevenBit/Conf/IObject.hpp" -#include "SevenBit/Conf/InMemoryConfiguration.hpp" -#include "SevenBit/Conf/JsonConfiguration.hpp" -#include "SevenBit/Conf/JsonFileConfiguration.hpp" -#include "SevenBit/Conf/JsonStreamConfiguration.hpp" -#include "SevenBit/Conf/KeyPerFileConfiguration.hpp" #include "SevenBit/Conf/SettingParserBuilder.hpp" #include "SevenBit/Conf/SettingParserConfig.hpp" +#include "SevenBit/Conf/Sources/AppSettingsConfiguration.hpp" +#include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" +#include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" +#include "SevenBit/Conf/Sources/InMemoryConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonStreamConfiguration.hpp" +#include "SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/ConfigurationBuilder.hpp b/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp similarity index 96% rename from Include/SevenBit/Conf/Details/Impl/ConfigurationBuilder.hpp rename to Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp index 1457464..44b7b97 100644 --- a/Include/SevenBit/Conf/Details/Impl/ConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp @@ -1,9 +1,10 @@ #pragma once -#include "SevenBit/Conf/Configuration.hpp" #include "SevenBit/Conf/ConfigurationBuilder.hpp" +#include "SevenBit/Conf/Details/Configuration.hpp" #include "SevenBit/Conf/Details/Utils.hpp" + namespace sb::cf { INLINE ConfigurationBuilder::ConfigurationBuilder(std::vector sources) diff --git a/Include/SevenBit/Conf/Details/Impl/Exceptions.hpp b/Include/SevenBit/Conf/Impl/Exceptions.hpp similarity index 100% rename from Include/SevenBit/Conf/Details/Impl/Exceptions.hpp rename to Include/SevenBit/Conf/Impl/Exceptions.hpp diff --git a/Include/SevenBit/Conf/Details/Impl/SettingParserBuilder.hpp b/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp similarity index 100% rename from Include/SevenBit/Conf/Details/Impl/SettingParserBuilder.hpp rename to Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp diff --git a/Include/SevenBit/Conf/LibraryConfig.hpp b/Include/SevenBit/Conf/LibraryConfig.hpp index 1ba4b0d..44d3d03 100644 --- a/Include/SevenBit/Conf/LibraryConfig.hpp +++ b/Include/SevenBit/Conf/LibraryConfig.hpp @@ -2,29 +2,53 @@ #include "SevenBit/Conf/CmakeDef.hpp" -#ifndef _7BIT_CONF_VERSION +#ifndef _7BIT_CONF_VERSION_MAJOR +#define _7BIT_CONF_VERSION_MAJOR 0 +#endif -#define _7BIT_CONF_VERSION "0.0.0" +#ifndef _7BIT_CONF_VERSION_MINOR +#define _7BIT_CONF_VERSION_MINOR 0 +#endif +#ifndef _7BIT_CONF_VERSION_PATCH +#define _7BIT_CONF_VERSION_PATCH 0 #endif -#if !defined _7BIT_CONF_SHARED_LIB && !defined _7BIT_CONF_STATIC_LIB && !defined _7BIT_CONF_HEADER_ONLY_LIB +#define _7BIT_CONF_VERSION_AS_NUMBER \ + (_7BIT_CONF_VERSION_MAJOR * 10000 + _7BIT_CONF_VERSION_MINOR * 100 + _7BIT_CONF_VERSION_PATCH) -#define _7BIT_CONF_HEADER_ONLY_LIB +#define _7BIT_CONF_MACRO_TO_STRING(macro) _7BIT_CONF_TO_STRING(macro) +#define _7BIT_CONF_TO_STRING(value) #value -#endif +#define _7BIT_CONF_VERSION \ + _7BIT_CONF_MACRO_TO_STRING(_7BIT_CONF_VERSION_MAJOR) \ + "." _7BIT_CONF_MACRO_TO_STRING(_7BIT_CONF_VERSION_MINOR) "." _7BIT_CONF_MACRO_TO_STRING(_7BIT_CONF_VERSION_PATCH) -#ifdef _7BIT_CONF_HEADER_ONLY_LIB +#if defined _7BIT_CONF_STATIC_LIB #undef _7BIT_CONF_SHARED_LIB +#undef _7BIT_CONF_HEADER_ONLY_LIB + +#define INLINE + +#elif defined _7BIT_CONF_SHARED_LIB + #undef _7BIT_CONF_STATIC_LIB +#undef _7BIT_CONF_HEADER_ONLY_LIB -#define _7BIT_CONF_ADD_IMPL -#define INLINE inline +#define INLINE #else -#define INLINE +#ifndef _7BIT_CONF_HEADER_ONLY_LIB +#define _7BIT_CONF_HEADER_ONLY_LIB +#endif + +#undef _7BIT_CONF_SHARED_LIB +#undef _7BIT_CONF_STATIC_LIB + +#define _7BIT_CONF_ADD_IMPL +#define INLINE inline #endif diff --git a/Include/SevenBit/Conf/SettingParserBuilder.hpp b/Include/SevenBit/Conf/SettingParserBuilder.hpp index 3b3e7cd..6db05a3 100644 --- a/Include/SevenBit/Conf/SettingParserBuilder.hpp +++ b/Include/SevenBit/Conf/SettingParserBuilder.hpp @@ -47,5 +47,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/SettingParserBuilder.hpp" +#include "SevenBit/Conf/Impl/SettingParserBuilder.hpp" #endif diff --git a/Include/SevenBit/Conf/AppSettingsConfiguration.hpp b/Include/SevenBit/Conf/Sources/AppSettingsConfiguration.hpp similarity index 92% rename from Include/SevenBit/Conf/AppSettingsConfiguration.hpp rename to Include/SevenBit/Conf/Sources/AppSettingsConfiguration.hpp index b8df519..5b243f8 100644 --- a/Include/SevenBit/Conf/AppSettingsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/AppSettingsConfiguration.hpp @@ -29,5 +29,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/AppSettingsConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/AppSettingsConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/ChainedConfiguration.hpp b/Include/SevenBit/Conf/Sources/ChainedConfiguration.hpp similarity index 92% rename from Include/SevenBit/Conf/ChainedConfiguration.hpp rename to Include/SevenBit/Conf/Sources/ChainedConfiguration.hpp index c9ed933..e120261 100644 --- a/Include/SevenBit/Conf/ChainedConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/ChainedConfiguration.hpp @@ -5,8 +5,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -49,5 +49,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/ChainedConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp similarity index 93% rename from Include/SevenBit/Conf/CommandLineConfiguration.hpp rename to Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp index 2c09e51..e772403 100644 --- a/Include/SevenBit/Conf/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp @@ -4,9 +4,9 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" #include "SevenBit/Conf/SettingParserBuilder.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -49,5 +49,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/CommandLineConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/ConfigurationProviderBase.hpp b/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp similarity index 92% rename from Include/SevenBit/Conf/ConfigurationProviderBase.hpp rename to Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp index 93ada2b..7352527 100644 --- a/Include/SevenBit/Conf/ConfigurationProviderBase.hpp +++ b/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp @@ -32,5 +32,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/ConfigurationProviderBase.hpp" +#include "SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp" #endif diff --git a/Include/SevenBit/Conf/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp similarity index 92% rename from Include/SevenBit/Conf/EnvironmentVarsConfiguration.hpp rename to Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp index 494ecc8..91c4cb1 100644 --- a/Include/SevenBit/Conf/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp @@ -5,10 +5,10 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" #include "SevenBit/Conf/ISettingParser.hpp" #include "SevenBit/Conf/SettingParserBuilder.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -53,5 +53,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/EnvironmentVarsConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/Details/Impl/AppSettingsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/AppSettingsConfiguration.hpp similarity index 84% rename from Include/SevenBit/Conf/Details/Impl/AppSettingsConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/AppSettingsConfiguration.hpp index a435641..07c586e 100644 --- a/Include/SevenBit/Conf/Details/Impl/AppSettingsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/AppSettingsConfiguration.hpp @@ -2,9 +2,10 @@ #include -#include "SevenBit/Conf/AppSettingsConfiguration.hpp" -#include "SevenBit/Conf/ChainedConfiguration.hpp" -#include "SevenBit/Conf/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/AppSettingsConfiguration.hpp" +#include "SevenBit/Conf/Sources/ChainedConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" + namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/ChainedConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp similarity index 96% rename from Include/SevenBit/Conf/Details/Impl/ChainedConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp index 3b9b801..337dfe8 100644 --- a/Include/SevenBit/Conf/Details/Impl/ChainedConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp @@ -2,9 +2,10 @@ #include -#include "SevenBit/Conf/ChainedConfiguration.hpp" #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/ChainedConfiguration.hpp" + namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp similarity index 97% rename from Include/SevenBit/Conf/Details/Impl/CommandLineConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp index 413ff2e..4018331 100644 --- a/Include/SevenBit/Conf/Details/Impl/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp @@ -1,7 +1,8 @@ #pragma once -#include "SevenBit/Conf/CommandLineConfiguration.hpp" #include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" + namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/ConfigurationProviderBase.hpp b/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp similarity index 93% rename from Include/SevenBit/Conf/Details/Impl/ConfigurationProviderBase.hpp rename to Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp index 09a8927..7b412f6 100644 --- a/Include/SevenBit/Conf/Details/Impl/ConfigurationProviderBase.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp @@ -2,9 +2,10 @@ #include -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/Details/JsonExt.hpp" #include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" + namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp similarity index 97% rename from Include/SevenBit/Conf/Details/Impl/EnvironmentVarsConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp index b2b49cf..00499a6 100644 --- a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp @@ -3,8 +3,9 @@ #include #include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/EnvironmentVarsConfiguration.hpp" #include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" + #ifdef _WIN32 extern "C" __declspec(dllimport) char **_environ; diff --git a/Include/SevenBit/Conf/Details/Impl/InMemoryConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp similarity index 95% rename from Include/SevenBit/Conf/Details/Impl/InMemoryConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp index 5268d5e..53e9ace 100644 --- a/Include/SevenBit/Conf/Details/Impl/InMemoryConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp @@ -2,7 +2,7 @@ #include "SevenBit/Conf/Details/JsonExt.hpp" #include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/InMemoryConfiguration.hpp" +#include "SevenBit/Conf/Sources/InMemoryConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/JsonConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp similarity index 94% rename from Include/SevenBit/Conf/Details/Impl/JsonConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp index 1d32be7..ac83e84 100644 --- a/Include/SevenBit/Conf/Details/Impl/JsonConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp @@ -1,7 +1,7 @@ #pragma once #include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/JsonFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp similarity index 97% rename from Include/SevenBit/Conf/Details/Impl/JsonFileConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp index 28271ab..b940ff6 100644 --- a/Include/SevenBit/Conf/Details/Impl/JsonFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp @@ -5,7 +5,7 @@ #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/JsonStreamConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp similarity index 95% rename from Include/SevenBit/Conf/Details/Impl/JsonStreamConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp index b738163..e80dc70 100644 --- a/Include/SevenBit/Conf/Details/Impl/JsonStreamConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp @@ -4,7 +4,7 @@ #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/JsonStreamConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonStreamConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/KeyPerFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp similarity index 94% rename from Include/SevenBit/Conf/Details/Impl/KeyPerFileConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp index fe11e54..84ae8b0 100644 --- a/Include/SevenBit/Conf/Details/Impl/KeyPerFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp @@ -1,11 +1,11 @@ #pragma once -#include "SevenBit/Conf/ChainedConfiguration.hpp" #include "SevenBit/Conf/Details/JsonExt.hpp" #include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/JsonFileConfiguration.hpp" -#include "SevenBit/Conf/KeyPerFileConfiguration.hpp" -#include "SevenBit/Conf/MapConfiguration.hpp" +#include "SevenBit/Conf/Sources/ChainedConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/MapConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/Impl/MapConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp similarity index 97% rename from Include/SevenBit/Conf/Details/Impl/MapConfiguration.hpp rename to Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp index 09f9b7d..96f17fc 100644 --- a/Include/SevenBit/Conf/Details/Impl/MapConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp @@ -1,7 +1,7 @@ #pragma once #include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/MapConfiguration.hpp" +#include "SevenBit/Conf/Sources/MapConfiguration.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/InMemoryConfiguration.hpp b/Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp similarity index 91% rename from Include/SevenBit/Conf/InMemoryConfiguration.hpp rename to Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp index 7773bfd..0b0e732 100644 --- a/Include/SevenBit/Conf/InMemoryConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp @@ -7,8 +7,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -46,5 +46,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/InMemoryConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/JsonConfiguration.hpp b/Include/SevenBit/Conf/Sources/JsonConfiguration.hpp similarity index 90% rename from Include/SevenBit/Conf/JsonConfiguration.hpp rename to Include/SevenBit/Conf/Sources/JsonConfiguration.hpp index e7f667c..df676b9 100644 --- a/Include/SevenBit/Conf/JsonConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/JsonConfiguration.hpp @@ -4,8 +4,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -41,5 +41,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/JsonFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/JsonFileConfiguration.hpp similarity index 91% rename from Include/SevenBit/Conf/JsonFileConfiguration.hpp rename to Include/SevenBit/Conf/Sources/JsonFileConfiguration.hpp index 274483c..0316cc7 100644 --- a/Include/SevenBit/Conf/JsonFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/JsonFileConfiguration.hpp @@ -5,8 +5,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -48,5 +48,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/JsonStreamConfiguration.hpp b/Include/SevenBit/Conf/Sources/JsonStreamConfiguration.hpp similarity index 90% rename from Include/SevenBit/Conf/JsonStreamConfiguration.hpp rename to Include/SevenBit/Conf/Sources/JsonStreamConfiguration.hpp index 38540dd..7369502 100644 --- a/Include/SevenBit/Conf/JsonStreamConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/JsonStreamConfiguration.hpp @@ -4,8 +4,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -44,5 +44,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/JsonStreamConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/KeyPerFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp similarity index 93% rename from Include/SevenBit/Conf/KeyPerFileConfiguration.hpp rename to Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp index 7ffe796..67723d3 100644 --- a/Include/SevenBit/Conf/KeyPerFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp @@ -7,8 +7,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -52,5 +52,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/KeyPerFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp" #endif diff --git a/Include/SevenBit/Conf/MapConfiguration.hpp b/Include/SevenBit/Conf/Sources/MapConfiguration.hpp similarity index 92% rename from Include/SevenBit/Conf/MapConfiguration.hpp rename to Include/SevenBit/Conf/Sources/MapConfiguration.hpp index 736090f..2371ec8 100644 --- a/Include/SevenBit/Conf/MapConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/MapConfiguration.hpp @@ -5,9 +5,9 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" #include "SevenBit/Conf/Json.hpp" +#include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf { @@ -46,5 +46,5 @@ namespace sb::cf } // namespace sb::cf #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/MapConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/MapConfiguration.hpp" #endif diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 99897b8..cb4a212 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -2,40 +2,34 @@ include(GenerateExportHeader) find_package(taocpp-json QUIET) + if(NOT taocpp-json_FOUND) include(FetchContent) FetchContent_Declare( - taocpp-json - GIT_REPOSITORY https://github.com/taocpp/json.git - GIT_TAG 1.0.0-beta.14 - OVERRIDE_FIND_PACKAGE + taocpp-json + GIT_REPOSITORY https://github.com/taocpp/json.git + GIT_TAG 1.0.0-beta.14 + OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(taocpp-json) endif() + find_package(taocpp-json REQUIRED) -if (_7BIT_CONF_SHARED_LIB) - add_library(7bitConf SHARED - Source.cpp ${_7BIT_CONF_ALL_HEADERS} - ) -elseif (_7BIT_CONF_STATIC_LIB) - add_library(7bitConf STATIC - Source.cpp ${_7BIT_CONF_ALL_HEADERS} - ) -elseif (_7BIT_CONF_HEADER_ONLY_LIB) +if(_7BIT_CONF_SHARED_LIB) + add_library(7bitConf SHARED Source.cpp) +elseif(_7BIT_CONF_STATIC_LIB) + add_library(7bitConf STATIC Source.cpp) +elseif(_7BIT_CONF_HEADER_ONLY_LIB) add_library(7bitConf INTERFACE) -endif () +endif() -if (_7BIT_CONF_HEADER_ONLY_LIB) - target_link_libraries(7bitConf INTERFACE - taocpp-json - ) -else () - target_link_libraries(7bitConf - taocpp-json - ) -endif () +if(_7BIT_CONF_HEADER_ONLY_LIB) + target_link_libraries(7bitConf INTERFACE taocpp-json) +else() + target_link_libraries(7bitConf taocpp-json) +endif() add_library(7bitConf::7bitConf ALIAS 7bitConf) @@ -44,5 +38,5 @@ set_target_properties(7bitConf PROPERTIES DEBUG_POSTFIX d) string(REPLACE ";" "$" dirs "${_7BIT_CONF_INCLUDE_DIR}") target_include_directories(7bitConf INTERFACE - $ - "$/${CMAKE_INSTALL_INCLUDEDIR}>") + $ + "$/${CMAKE_INSTALL_INCLUDEDIR}>") diff --git a/Source/Source.cpp b/Source/Source.cpp index 9600b44..7be1443 100644 --- a/Source/Source.cpp +++ b/Source/Source.cpp @@ -1,25 +1,24 @@ #include "ConfigCheck.hpp" -#include "SevenBit/Conf/Details/Impl/AppSettingsConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/ChainedConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/CommandLineConfiguration.hpp" #include "SevenBit/Conf/Details/Impl/Configuration.hpp" -#include "SevenBit/Conf/Details/Impl/ConfigurationBuilder.hpp" -#include "SevenBit/Conf/Details/Impl/ConfigurationManager.hpp" -#include "SevenBit/Conf/Details/Impl/ConfigurationProviderBase.hpp" #include "SevenBit/Conf/Details/Impl/Deserializers.hpp" -#include "SevenBit/Conf/Details/Impl/EnvironmentVarsConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/Exceptions.hpp" -#include "SevenBit/Conf/Details/Impl/InMemoryConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/JsonConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/JsonFileConfiguration.hpp" #include "SevenBit/Conf/Details/Impl/JsonObjectExt.hpp" -#include "SevenBit/Conf/Details/Impl/JsonStreamConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/KeyPerFileConfiguration.hpp" -#include "SevenBit/Conf/Details/Impl/MapConfiguration.hpp" #include "SevenBit/Conf/Details/Impl/SettingParser.hpp" -#include "SevenBit/Conf/Details/Impl/SettingParserBuilder.hpp" #include "SevenBit/Conf/Details/Impl/SettingSplitter.hpp" #include "SevenBit/Conf/Details/Impl/Utils.hpp" #include "SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp" +#include "SevenBit/Conf/Impl/ConfigurationBuilder.hpp" +#include "SevenBit/Conf/Impl/Exceptions.hpp" +#include "SevenBit/Conf/Impl/SettingParserBuilder.hpp" +#include "SevenBit/Conf/Sources//Impl/AppSettingsConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/ChainedConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/CommandLineConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/ConfigurationProviderBase.hpp" +#include "SevenBit/Conf/Sources//Impl/EnvironmentVarsConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/InMemoryConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/JsonStreamConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/KeyPerFileConfiguration.hpp" +#include "SevenBit/Conf/Sources//Impl/MapConfiguration.hpp" diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index e59daed..363eb25 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2,13 +2,31 @@ include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # release-1.14.0 + GIT_TAG v1.14.0 ) +set(gtest_build_tests OFF) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -enable_testing() +include_directories(./Helpers) -add_subdirectory(EndToEnd) -add_subdirectory(Integration) -add_subdirectory(Unit) \ No newline at end of file +if(_7BIT_CONF_BUILD_UNIT_TESTS) + add_subdirectory(Unit) +endif() + +if(_7BIT_CONF_BUILD_INTEGRATION_TESTS) + add_subdirectory(Integration) +endif() + +if(_7BIT_CONF_BUILD_E2E_TESTS) + add_subdirectory(EndToEnd) +endif() + +file(GLOB_RECURSE FILES CONFIGURE_DEPENDS ./Helpers/Files/*) +file(GLOB_RECURSE FILES_DIR CONFIGURE_DEPENDS ./Helpers/Files/Directory/*) + +file(COPY ${FILES} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +file(COPY ${FILES_DIR} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Directory) + +file(COPY ${FILES} DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${FILES_DIR} DESTINATION ${CMAKE_BINARY_DIR}/Directory) \ No newline at end of file diff --git a/Tests/EndToEnd/CMakeLists.txt b/Tests/EndToEnd/CMakeLists.txt new file mode 100644 index 0000000..203048e --- /dev/null +++ b/Tests/EndToEnd/CMakeLists.txt @@ -0,0 +1,7 @@ + +if(_7BIT_DI_BUILD_EXAMPLES) + find_package(Python REQUIRED) + + # add_test(NAME EndToEnd.Examples COMMAND ${Python_EXECUTABLE} test_examples.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Examples) +endif() diff --git a/Tests/EndToEnd/Examples/expectedTestData.json b/Tests/EndToEnd/Examples/expectedTestData.json new file mode 100644 index 0000000..aa32ea6 --- /dev/null +++ b/Tests/EndToEnd/Examples/expectedTestData.json @@ -0,0 +1,14 @@ +{ + "Cli": "", + "CoreClasses": "", + "ExternalSingleton": "^Hello from service!", + "FactoryFunctions": "^Hello from service!", + "InjectingMultipleServices": "^work all: work A done! work B done! work C done! \nsingle work: work C done!", + "InjectingServiceProvider": "^actionA, actionB executed.", + "InjectingServices": "^actionA, actionB executed.", + "InjectionRules": "", + "SeparateImplementation": "^Hello from service.", + "ServicesLifeTime": "\nSingletons comparison\nrootProvider\\s+== rootProvider:\\s+1\nrootProvider\\s+== scopedProvider:\\s+1\nscopedProvider\\s+== scopedProvider:\\s+1\n\nScoped comparison\nrootProvider\\s+== rootProvider:\\s+1\nrootProvider\\s+== scopedProvider:\\s+0\nscopedProvider\\s+== scopedProvider:\\s+1\n\nTransient comparison\nrootProvider\\s+== rootProvider:\\s+0\nrootProvider\\s+== scopedProvider:\\s+0\nscopedProvider\\s+== scopedProvider:\\s+0\n", + "Simple": "^part a done!\npart b done!\n", + "ServiceAliases": "actionA from top service, actionB from top service executed." +} diff --git a/Tests/EndToEnd/Examples/test_examples.py b/Tests/EndToEnd/Examples/test_examples.py new file mode 100644 index 0000000..5e664dc --- /dev/null +++ b/Tests/EndToEnd/Examples/test_examples.py @@ -0,0 +1,69 @@ +import sys +import os +import subprocess +import re +import json + + +def getBinDir(): + if len(sys.argv) != 2: + raise Exception("binary directory not provided") + binDir = sys.argv[1] + if not os.path.exists(binDir): + raise Exception("Binary directory does not exist") + return binDir + + +class ExamplesTest: + def __init__(self, binDir): + self.binDir = binDir + self.examplesPaths = self.__getAvailableExamples() + self.expectedTestMap = self.__getExpectedTestMap() + + def __getAvailableExamples(self): + paths = [os.path.join(self.binDir, f) for f in os.listdir(self.binDir) if + "Example" in f and os.path.splitext(f)[1] in [".exe", ""]] + return [f for f in paths if os.path.isfile(f)] + + def __getExpectedTestMap(self): + with open('expectedTestData.json') as data: + return json.load(data) + + def __runTest(self, examplePath, expected): + if expected is None: + raise Exception(f"example was not expected") + result = subprocess.run([examplePath], capture_output=True, text=True) + if result.returncode: + raise Exception(f"example returned non zero code {result.returncode}") + if not re.search(expected, result.stdout): + raise Exception(f"result of running example: '{result.stdout}' does not match expected: '{expected}'") + + def __runTestAndSummarize(self, examplePath): + fileName = os.path.basename(examplePath) + name = os.path.splitext(fileName)[0].replace("Example", "") + try: + self.__runTest(examplePath, self.expectedTestMap.get(name)) + print(f"{name} example test succeeded") + return True + except Exception as e: + print(f"{name} example test failed: {e}") + finally: + del self.expectedTestMap[name] + return False + + def run(self): + allTests = len(self.examplesPaths) + succeededTests = 0 + for count, examplePath in enumerate(self.examplesPaths, start=1): + print(f"Test {count}/{allTests}") + succeededTests += self.__runTestAndSummarize(examplePath) + if len(self.expectedTestMap): + raise Exception(f"Some expected tests were not run: {self.expectedTestMap.keys()}") + if succeededTests == allTests: + print(f"All test succeeded: {succeededTests}/{allTests}") + else: + raise Exception(f"Some tests failed: {allTests - succeededTests}/{allTests}") + + +if __name__ == "__main__": + ExamplesTest(getBinDir()).run() diff --git a/Tests/Helpers/Classes/CustomConfigSource.hpp b/Tests/Helpers/Classes/CustomConfigSource.hpp new file mode 100644 index 0000000..e50f561 --- /dev/null +++ b/Tests/Helpers/Classes/CustomConfigSource.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "SevenBit/Conf/IConfigurationBuilder.hpp" +#include "SevenBit/Conf/IConfigurationProvider.hpp" +#include "SevenBit/Conf/IConfigurationSource.hpp" +#include "SevenBit/Conf/ObjectHolder.hpp" +#include "SevenBit/Conf/Sources/JsonConfiguration.hpp" + + +class CustomConfigSource : public sb::cf::IConfigurationSource +{ + sb::cf::IConfigurationProvider::Ptr build(sb::cf::IConfigurationBuilder &builder) + { + sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get()++; + + return sb::cf::JsonConfigurationSource::create({})->build(builder); + } +}; diff --git a/Tests/Helpers/Files/Directory/settingOne.json b/Tests/Helpers/Files/Directory/settingOne.json new file mode 100644 index 0000000..aab360d --- /dev/null +++ b/Tests/Helpers/Files/Directory/settingOne.json @@ -0,0 +1,9 @@ +{ + "number": 12345, + "array": [1, 2, 3, 4, 5, 6], + "string": "string", + "object": { + "num": 134, + "string": "string" + } +} diff --git a/Tests/Helpers/Files/Directory/settingTwo.json b/Tests/Helpers/Files/Directory/settingTwo.json new file mode 100644 index 0000000..cf81edc --- /dev/null +++ b/Tests/Helpers/Files/Directory/settingTwo.json @@ -0,0 +1,10 @@ +{ + "array": [1], + "string": "stringdev", + "object": { + "string": "stringdev", + "inner": { + "num": 12345 + } + } +} diff --git a/Tests/Helpers/Files/appsettings.dev.json b/Tests/Helpers/Files/appsettings.dev.json new file mode 100644 index 0000000..f82d6e9 --- /dev/null +++ b/Tests/Helpers/Files/appsettings.dev.json @@ -0,0 +1,10 @@ +{ + "Array": [11], + "MySetting": "appsettings.dev.json Value", + "ExtraSetting": "extra appsettings.dev.json Value", + "Logging": { + "LogLevel": { + "Default": "Warning" + } + } +} diff --git a/Tests/Helpers/Files/appsettings.json b/Tests/Helpers/Files/appsettings.json new file mode 100644 index 0000000..38aa15b --- /dev/null +++ b/Tests/Helpers/Files/appsettings.json @@ -0,0 +1,9 @@ +{ + "Array": [1, 2, 3, 4, 5], + "MySetting": "appsettings.json Value", + "Logging": { + "LogLevel": { + "Default": "Information" + } + } +} diff --git a/Tests/Helpers/Files/bad.json b/Tests/Helpers/Files/bad.json new file mode 100644 index 0000000..e5c73e4 --- /dev/null +++ b/Tests/Helpers/Files/bad.json @@ -0,0 +1 @@ +"bad" diff --git a/Tests/Helpers/Mocks/ConfigurationBuilderMock.hpp b/Tests/Helpers/Mocks/ConfigurationBuilderMock.hpp new file mode 100644 index 0000000..a2305bc --- /dev/null +++ b/Tests/Helpers/Mocks/ConfigurationBuilderMock.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "SevenBit/Conf/IConfigurationBuilder.hpp" + +struct ConfigurationBuilderMock : public sb::cf::IConfigurationBuilder +{ + MOCK_METHOD((sb::cf::IConfigurationBuilder &), add, (sb::cf::IConfigurationSource::SPtr), (override)); + MOCK_METHOD((std::unordered_map &), getProperties, (), (override)); + MOCK_METHOD((const std::unordered_map &), getProperties, (), (const override)); + MOCK_METHOD((std::vector &), getSources, (), (override)); + MOCK_METHOD((const std::vector &), getSources, (), (const override)); + MOCK_METHOD((sb::cf::IConfiguration::Ptr), build, (), (override)); + MOCK_METHOD((void), clear, (), (override)); +}; diff --git a/Tests/Helpers/Mocks/DeserializerMock.hpp b/Tests/Helpers/Mocks/DeserializerMock.hpp new file mode 100644 index 0000000..7d4f5f4 --- /dev/null +++ b/Tests/Helpers/Mocks/DeserializerMock.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "SevenBit/Conf/IDeserializer.hpp" + +struct DeserializerMock : public sb::cf::IDeserializer +{ + MOCK_METHOD((sb::cf::JsonValue), deserialize, (std::optional), (const override)); +}; diff --git a/Tests/Helpers/Mocks/SettingSplitterMock.hpp b/Tests/Helpers/Mocks/SettingSplitterMock.hpp new file mode 100644 index 0000000..131e738 --- /dev/null +++ b/Tests/Helpers/Mocks/SettingSplitterMock.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#include "SevenBit/Conf/ISettingSplitter.hpp" + +struct SettingSplitterMock : public sb::cf::ISettingSplitter +{ + MOCK_METHOD((sb::cf::ISettingSplitter::Result), split, (std::string_view), (const override)); +}; diff --git a/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp b/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp new file mode 100644 index 0000000..1fe4350 --- /dev/null +++ b/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "SevenBit/Conf/IValueDeserializersMap.hpp" + +struct ValueDeserializersMapMock : public sb::cf::IValueDeserializersMap +{ + MOCK_METHOD((const sb::cf::IDeserializer *), getDeserializerFor, (std::string_view), (const override)); +}; diff --git a/Tests/Helpers/Utilities/ParamsTest.hpp b/Tests/Helpers/Utilities/ParamsTest.hpp new file mode 100644 index 0000000..09f0e42 --- /dev/null +++ b/Tests/Helpers/Utilities/ParamsTest.hpp @@ -0,0 +1,53 @@ + +#include +#include +#include + +#include "SevenBit/Conf/Json.hpp" + +template using Param = std::tuple; +template using Params = std::vector>; +template using OneParams = std::vector; + +template struct TypeExtractor +{ + using value_type = typename T::iterator::value_type; +}; + +template struct TypeExtractor> +{ + using value_type = typename std::tuple::value_type...>; +}; +#define SETUP_PARAMS_TEST(group, name, values) \ + struct name : public testing::TestWithParam::value_type> \ + { \ + }; \ + INSTANTIATE_TEST_SUITE_P(group, name, values); + +#define PARAMS_TEST(group, name, values) \ + SETUP_PARAMS_TEST(group, name, testing::ValuesIn(values)) \ + TEST_P(name, ) + +#define PARAMS_TEST_COMBINED_1(group, name, one) \ + SETUP_PARAMS_TEST(group, name, testing::Combine(testing::ValuesIn(one))) TEST_P(name, ) + +#define PARAMS_TEST_COMBINED_2(group, name, one, two) \ + SETUP_PARAMS_TEST(group, name, testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two))) \ + TEST_P(name, ) + +#define PARAMS_TEST_COMBINED_3(group, name, one, two, three) \ + SETUP_PARAMS_TEST(group, name, \ + testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two), testing::ValuesIn(three))) \ + TEST_P(name, ) + +#define PARAMS_TEST_COMBINED_4(group, name, one, two, three, four) \ + SETUP_PARAMS_TEST(group, name, \ + testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two), testing::ValuesIn(three), \ + testing::ValuesIn(four))) \ + TEST_P(name, ) + +#define PARAMS_TEST_COMBINED_5(group, name, one, two, three, four, five) \ + SETUP_PARAMS_TEST(group, name, \ + testing::Combine(testing::ValuesIn(one), testing::ValuesIn(two), testing::ValuesIn(three), \ + testing::ValuesIn(four), testing::ValuesIn(five))) \ + TEST_P(name, ) diff --git a/Tests/Integration/CMakeLists.txt b/Tests/Integration/CMakeLists.txt new file mode 100644 index 0000000..42f8b37 --- /dev/null +++ b/Tests/Integration/CMakeLists.txt @@ -0,0 +1,15 @@ +file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp) + +add_executable(IntegrationTests + ${SOURCES} +) + +target_link_libraries(IntegrationTests PUBLIC + GTest::gtest + GTest::gmock + 7bitDI +) + +include(GoogleTest) +gtest_discover_tests(IntegrationTests + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/Tests/Integration/RunTests.cpp b/Tests/Integration/RunTests.cpp new file mode 100644 index 0000000..245b9f5 --- /dev/null +++ b/Tests/Integration/RunTests.cpp @@ -0,0 +1,10 @@ +#include + +int main(int argc, char *argv[]) +{ + // Run a specific test only + // testing::GTEST_FLAG(filter) = "Template.*"; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/Tests/Integration/TestTemplate.cpp b/Tests/Integration/TestTemplate.cpp new file mode 100644 index 0000000..c8f294e --- /dev/null +++ b/Tests/Integration/TestTemplate.cpp @@ -0,0 +1,19 @@ +#include + +class Template : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + Template() {} + + void SetUp() override {} + + void TearDown() override {} + + ~Template() override = default; + + static void TearDownTestSuite() {} +}; + +TEST_F(Template, ExampleTest) { EXPECT_TRUE(true); } diff --git a/Tests/Unit/AppSettingsConfigurationTest.cpp b/Tests/Unit/AppSettingsConfigurationTest.cpp new file mode 100644 index 0000000..6e55734 --- /dev/null +++ b/Tests/Unit/AppSettingsConfigurationTest.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Sources/AppSettingsConfiguration.hpp" + +class AppSettingsConfiguration : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + AppSettingsConfiguration() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(AppSettingsConfiguration, ShouldLoadAppSettings) +{ + auto provider = sb::cf::AppSettingsConfigurationSource::create()->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, + {"MySetting", "appsettings.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(AppSettingsConfiguration, ShouldLoadDevAppSettings) +{ + auto provider = sb::cf::AppSettingsConfigurationSource::create("dev")->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, + {"MySetting", "appsettings.dev.json Value"}, + {"ExtraSetting", "extra appsettings.dev.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} diff --git a/Tests/Unit/CMakeLists.txt b/Tests/Unit/CMakeLists.txt new file mode 100644 index 0000000..8386c79 --- /dev/null +++ b/Tests/Unit/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp) + +add_executable(UnitTests + ${SOURCES} +) + +target_link_libraries(UnitTests PUBLIC + GTest::gtest + GTest::gmock + 7bitConf +) + +file(GLOB_RECURSE FILES CONFIGURE_DEPENDS Files/*) +file(GLOB_RECURSE FILES_DIR CONFIGURE_DEPENDS Files/Directory/*) + +file(COPY ${FILES} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +file(COPY ${FILES_DIR} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Directory) + +file(COPY ${FILES} DESTINATION ${CMAKE_BINARY_DIR}) +file(COPY ${FILES_DIR} DESTINATION ${CMAKE_BINARY_DIR}/Directory) + +include(GoogleTest) +gtest_discover_tests(UnitTests + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) \ No newline at end of file diff --git a/Tests/Unit/ChainedConfigurationTest.cpp b/Tests/Unit/ChainedConfigurationTest.cpp new file mode 100644 index 0000000..c93f262 --- /dev/null +++ b/Tests/Unit/ChainedConfigurationTest.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/ChainedConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" + +class ChainedConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + ChainedConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(ChainedConfigurationTest, ShouldLoadEmptyChainConfig) +{ + auto provider = sb::cf::ChainedConfigurationSource::create()->build(mock); + + provider->load(); + + EXPECT_TRUE(provider->getConfiguration().empty()); +} + +TEST_F(ChainedConfigurationTest, ShouldFailCreationDueToNullSource) +{ + EXPECT_THROW(auto result = sb::cf::ChainedConfigurationSource::create({nullptr}), sb::cf::NullPointerException); +} + +TEST_F(ChainedConfigurationTest, ShouldFailAddDueToNullSource) +{ + auto source = + sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json")}); + + EXPECT_THROW(source->add(nullptr), sb::cf::NullPointerException); +} + +TEST_F(ChainedConfigurationTest, ShouldLoadSimpleChainedConfig) +{ + auto provider = + sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json")}) + ->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, + {"MySetting", "appsettings.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(ChainedConfigurationTest, ShouldLoadComplexChainedConfig) +{ + auto provider = + sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json"), + sb::cf::JsonFileConfigurationSource::create("appsettings.dev.json"), + sb::cf::JsonConfigurationSource::create({{"number", 1}})}) + ->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, + {"number", 1}, + {"MySetting", "appsettings.dev.json Value"}, + {"ExtraSetting", "extra appsettings.dev.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} diff --git a/Tests/Unit/CommandLineConfigurationTest.cpp b/Tests/Unit/CommandLineConfigurationTest.cpp new file mode 100644 index 0000000..fe14cea --- /dev/null +++ b/Tests/Unit/CommandLineConfigurationTest.cpp @@ -0,0 +1,93 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" + +class CommandLineConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + CommandLineConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) +{ + std::vector args; + EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), + sb::cf::NullPointerException); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) +{ + const char *const argv[] = { + "program/path", "--string___string=test", "--list:0!string=string", "double!double=1.22", + "true_bool!bool=-1", "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", + "--list:1=string1", "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", + "--int_list:1___int=22", "int_list__2!int=11", + }; + int size = sizeof(argv) / sizeof(char *); + auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"string", "test"}, + {"double", 1.22}, + {"false_bool", false}, + {"false_bool2", false}, + {"true_bool", true}, + {"true_bool2", true}, + {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"int_list", sb::cf::JsonArray{33, 22, 11}}, + {"object", {{"inner", {{"object", "string"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) +{ + std::vector args = { + "--string___string=test", "--list:0!string=string", "double!double=1.22", "true_bool!bool=-1", + "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", "--list:1=string1", + "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", "--int_list:1___int=22", + "int_list__2!int=11", + }; + auto provider = sb::cf::CommandLineConfigurationSource::create(args)->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"string", "test"}, + {"double", 1.22}, + {"false_bool", false}, + {"false_bool2", false}, + {"true_bool", true}, + {"true_bool2", true}, + {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"int_list", sb::cf::JsonArray{33, 22, 11}}, + {"object", {{"inner", {{"object", "string"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) +{ + const char *argv[] = { + "exec/path", + }; + int size = sizeof(argv) / sizeof(char *); + auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); + + provider->load(); + + EXPECT_TRUE(provider->getConfiguration().empty()); +} diff --git a/Tests/Unit/ConfigurationBuilderTest.cpp b/Tests/Unit/ConfigurationBuilderTest.cpp new file mode 100644 index 0000000..838b1f0 --- /dev/null +++ b/Tests/Unit/ConfigurationBuilderTest.cpp @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "Classes/CustomConfigSource.hpp" +#include "SevenBit/Conf/ConfigurationBuilder.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/ObjectHolder.hpp" + +class ConfigurationBuilderTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + ConfigurationBuilderTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(ConfigurationBuilderTest, ShouldFailCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::ConfigurationBuilder{{nullptr}}, sb::cf::NullPointerException); +} + +TEST_F(ConfigurationBuilderTest, ShouldFailAddDueToNullSource) +{ + auto builder = sb::cf::ConfigurationBuilder{}; + + EXPECT_THROW(builder.add(nullptr), sb::cf::NullPointerException); +} + +TEST_F(ConfigurationBuilderTest, ShouldFailBuildDueToNullSource) +{ + auto builder = sb::cf::ConfigurationBuilder{}; + + builder.getSources().emplace_back(nullptr); + + EXPECT_THROW(builder.build(), sb::cf::NullPointerException); +} + +TEST_F(ConfigurationBuilderTest, ShouldClearSources) +{ + auto builder = sb::cf::ConfigurationBuilder{}; + + builder.addAppSettings("dev"); + + EXPECT_FALSE(builder.getSources().empty()); + + builder.clear(); + + EXPECT_TRUE(builder.getSources().empty()); +} + +TEST_F(ConfigurationBuilderTest, ShouldBuildSimpleConfig) +{ + auto builder = sb::cf::ConfigurationBuilder{}; + + builder.addAppSettings("dev") + .addJson({{"string", 1}}) + .addCommandLine({"--string=2", "array!json=[3,2,1]"}) + .AddInMemory("set:set", 44444) + .addKeyPerFile("Directory"); + + EXPECT_NO_THROW(builder.build()); +} + +TEST_F(ConfigurationBuilderTest, ShouldBuildConfigWithProperties) +{ + auto builder = sb::cf::ConfigurationBuilder{}; + + builder.getProperties()["counter"] = sb::cf::ObjectHolder::from(0); + + auto conf = builder.addAppSettings("dev") + .addJson({{"string", 1}}) + .addCommandLine({"--string=2", "array=3,2,1"}) + .AddInMemory("set:set", 44444) + .addKeyPerFile("Directory") + .add(std::make_unique()) + .add(std::make_unique()) + .add(std::make_unique()) + .add(std::make_unique()) + .build(); + + auto cnt = sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get(); + + EXPECT_EQ(cnt, 4); +} diff --git a/Tests/Unit/ConfigurationTest.cpp b/Tests/Unit/ConfigurationTest.cpp new file mode 100644 index 0000000..3b9989c --- /dev/null +++ b/Tests/Unit/ConfigurationTest.cpp @@ -0,0 +1,94 @@ +#include +#include + +#include "SevenBit/Conf/ConfigurationBuilder.hpp" + +class ConfigurationTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + ConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(ConfigurationTest, ShouldLoadConfig) +{ + auto conf = sb::cf::ConfigurationBuilder{} + .addAppSettings("dev") + .addJson({{"string", 1}}) + .addCommandLine({"--string=2", "Array:0!int=33"}) + .AddInMemory("set:set", 44444) + .addKeyPerFile("Directory") + .build(); + + sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{33, 2, 3, 4, 5}}, + {"MySetting", "appsettings.dev.json Value"}, + {"ExtraSetting", "extra appsettings.dev.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}, + {"set", {{"set", 44444}}}, + {"string", "2"}, + {"settingOne", + {{"number", 12345}, + {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, + {"string", "string"}, + {"object", {{"num", 134}, {"string", "string"}}}}}, + {"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + + EXPECT_EQ(conf->root(), expected); +} + +TEST_F(ConfigurationTest, ShouldFindConfgValues) +{ + auto conf = sb::cf::ConfigurationBuilder{} + .addAppSettings("dev") + .addJson({{"string", 1}}) + .addCommandLine({"--string=2", "Array:0!int=33"}) + .AddInMemory("set:set", 44444) + .addKeyPerFile("Directory") + .build(); + + EXPECT_TRUE(conf->find("Array")); + EXPECT_TRUE(conf->find("MySetting")); + EXPECT_TRUE(conf->deepFind("settingOne:number")); + EXPECT_TRUE(conf->deepFind({"settingTwo", "array", "0"})); + EXPECT_TRUE(conf->deepFind("settingTwo:array:0")); + EXPECT_EQ(conf->at("Array"), (sb::cf::JsonArray{33, 2, 3, 4, 5})); + EXPECT_EQ(conf->deepAt("settingOne:number"), (std::int64_t{12345})); + EXPECT_EQ(conf->deepAt("settingTwo:array:0"), (std::int64_t{1})); + EXPECT_EQ(conf->operator[]("settingTwo:array:0"), (std::int64_t{1})); + EXPECT_EQ(conf->operator[]({"settingTwo", "array", "0"}), (std::int64_t{1})); +} + +TEST_F(ConfigurationTest, ShouldSerializeValues) +{ + auto conf = sb::cf::ConfigurationBuilder{} + .addJson({{"string", 1}}) + .addCommandLine({"--string=2", "Array:0!int=33"}) + .AddInMemory("set:set", 44444) + .build(); + + std::string expected = R"({"Array":[33,2,3,4,5],"MySetting":""})"; + std::ostringstream stream; + + EXPECT_EQ(conf->toString(), R"({ + "Array": [ + 33 + ], + "set": { + "set": 44444 + }, + "string": "2" +})"); + EXPECT_EQ(conf->toString(0, ""), R"({"Array": [33],"set": {"set": 44444},"string": "2"})"); + stream << *conf; + EXPECT_EQ(stream.str(), R"({"Array":[33],"set":{"set":44444},"string":"2"})"); +} diff --git a/Tests/Unit/DeserializersTest.cpp b/Tests/Unit/DeserializersTest.cpp new file mode 100644 index 0000000..28dc776 --- /dev/null +++ b/Tests/Unit/DeserializersTest.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include + +#include "SevenBit/Conf/Details/Deserializers.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +#include "Utilities/ParamsTest.hpp" + +class DeserializersTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + DeserializersTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap() +{ + sb::cf::details::ValueDeserializersMap deserializers; + deserializers.set("string", std::make_unique()); + deserializers.set("bool", std::make_unique()); + deserializers.set("int", std::make_unique()); + deserializers.set("double", std::make_unique()); + deserializers.set("uint", std::make_unique()); + deserializers.set("json", std::make_unique()); + deserializers.set("null", std::make_unique()); + return std::move(deserializers); +} + +static Params, sb::cf::JsonValue> DeserializeData = { + // Int + {"int", "12", std::int64_t{12}}, + {"int", "-12", std::int64_t{-12}}, + {"Int", "-12", std::int64_t{-12}}, + {"INT", "-12", std::int64_t{-12}}, + {"InT", "-12", std::int64_t{-12}}, + {"int", std::nullopt, std::int64_t{0}}, + {"int", "1", std::int64_t{1}}, + {"int", " 1", std::int64_t{1}}, + {"int", "\t\n 1", std::int64_t{1}}, + {"int", "1", std::int64_t{1}}, + {"Int", "1", std::int64_t{1}}, + {"InT", "1", std::int64_t{1}}, + {"INT", "1", std::int64_t{1}}, + {"int", "-1234", std::int64_t{-1234}}, + {"int", "1234", std::int64_t{1234}}, + {"int", "-223372036854775807", std::int64_t{-223372036854775807ll}}, + {"int", "223372036854775807", std::int64_t{223372036854775807ll}}, + + // UInt + {"uint", "12", std::uint64_t{12}}, + {"Uint", "12", std::uint64_t{12}}, + {"UINT", "12", std::uint64_t{12}}, + {"UiNt", "12", std::uint64_t{12}}, + {"uint", "1222", std::uint64_t{1222}}, + {"uint", std::nullopt, std::uint64_t{0}}, + {"uint", "1", std::uint64_t{1}}, + {"uint", " 1", std::uint64_t{1}}, + {"uint", "\t\n 1", std::uint64_t{1}}, + {"uInt", "1", std::uint64_t{1}}, + {"uInT", "1", std::uint64_t{1}}, + {"UINT", "1", std::uint64_t{1}}, + {"uint", "1234", std::uint64_t{1234}}, + {"uint", "223372036854775807", std::uint64_t{223372036854775807ll}}, + {"uint", "17446744073709551615", std::uint64_t{17446744073709551615ull}}, + + // Bool + {"bool", "true", true}, + {"BoOl", "true", true}, + {"bool", "1", true}, + {"bool", "-11", true}, + {"bool", "0", false}, + {"Bool", "true", true}, + {"BOOL", "true", true}, + {"bool", "True", true}, + {"bool", "TrUe", true}, + {"bool", " 1", true}, + {"bool", "\t\n 1", true}, + {"bool", "-1", true}, + {"bool", "-1123", true}, + {"bool", "1123", true}, + {"bool", "false", false}, + {"bool", "False", false}, + {"bool", "FAlSe", false}, + {"bool", std::nullopt, false}, + // String + {"string", "hello", std::string{"hello"}}, + {"String", "hello", std::string{"hello"}}, + {"STRING", "hello", std::string{"hello"}}, + {"STRING", "hello", std::string{"hello"}}, + {"String", "hello", std::string{"hello"}}, + {"StRinG", "hell\to", std::string{"hell\to"}}, + {"string", "hello", std::string{"hello"}}, + {"string", "", std::string{""}}, + {"string", std::nullopt, std::string{""}}, + // Double + {"double", "1.1", 1.1}, + {"Double", "1.1", 1.1}, + {"DOUBLE", "1.1", 1.1}, + {"DoUBle", "1.1", 1.1}, + {"double", "-11.1", -11.1}, + {"double", "1", 1.0}, + {"double", " 1", 1.0}, + {"double", "\t\n 1", 1.0}, + {"Double", "1", 1.0}, + {"dOuBle", "1", 1.0}, + {"DOUBLE", "1", 1.0}, + {"double", "1.123", 1.123}, + {"double", "-12.21", -12.21}, + {"double", "10000.11", 10000.11}, + {"double", "-10000.11", -10000.11}, + {"double", std::nullopt, 0.0}, + // Null + {"null", "hello", sb::cf::json::null}, + {"Null", "hello", sb::cf::json::null}, + {"NULL", "hello", sb::cf::json::null}, + {"nULl", "hello", sb::cf::json::null}, + {"null", "1", sb::cf::json::null}, + {"null", "asdqwdwq", sb::cf::json::null}, + {"Null", "1", sb::cf::json::null}, + {"NULL", "1asdqwdwq", sb::cf::json::null}, + {"NuLl", "1.123 asdqwdwq", sb::cf::json::null}, + {"null", std::nullopt, sb::cf::json::null}, + // Json + {"json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"Json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"JSON", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"jSOn", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"JSon", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"JsOn", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"json", R"({"hello": [1]})", sb::cf::JsonObject{{"hello", sb::cf::JsonArray{1}}}}, + {"json", R"({"hello": null})", sb::cf::JsonObject{{"hello", sb::cf::json::null}}}, + +}; +PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) +{ + const auto &[type, value, expected] = GetParam(); + auto deserializers = makeDefaultDeserializersMap(); + + auto deserializer = deserializers.getDeserializerFor(type); + EXPECT_TRUE(deserializer); + EXPECT_EQ(deserializer->deserialize(value), expected); +} + +TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) +{ + auto deserializers = makeDefaultDeserializersMap(); + + auto deserializer = deserializers.getDeserializerFor("json"); + EXPECT_EQ((deserializer->deserialize(std::nullopt)), (sb::cf::JsonValue{})); +} + +TEST_F(DeserializersTest, ShouldNotFoundDeserializer) +{ + auto deserializers = makeDefaultDeserializersMap(); + + deserializers.set("unknown2", nullptr); + + EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); + EXPECT_FALSE(deserializers.getDeserializerFor("unknown")); + EXPECT_FALSE(deserializers.getDeserializerFor("unknown2")); +} + +static Params> FailDeserializeValues = { + // Int + {"int", "123 abcd"}, + {"int", "123 "}, + {"int", "123.123"}, + {"int", " as 123 abcd"}, + {"int", "abcd"}, + {"int", " "}, + {"int", ""}, + {"int", "\n"}, + // UInt + {"uint", "123 abcd"}, + {"uint", "123 "}, + {"uint", "-12"}, + {"uint", "123.123"}, + {"uint", " as 123 abcd"}, + {"uint", "abcd"}, + {"uint", " "}, + {"uint", ""}, + {"uint", "\n"}, + // Bool + {"bool", "123 abcd"}, + {"bool", "123.123"}, + {"bool", " as 123 abcd"}, + {"bool", "abcd"}, + {"bool", " "}, + {"bool", ""}, + {"bool", "\n"}, + {"bool", "ttruee"}, + {"bool", "ffalsee"}, + {"bool", "false "}, + {"bool", "false "}, + {"bool", " false"}, + // Double + {"double", "123 abcd"}, + {"double", "123 "}, + {"double", "123 "}, + {"double", " as 123 abcd"}, + {"double", "abcd"}, + {"double", " "}, + {"double", ""}, + {"double", "\n"}, + // Json + {"json", "123 abcd"}, + {"json", " as 123 abcd"}, + {"json", "abcd"}, + {"json", "{\"hello: 123}"}, + {"json", "{\"hello\": sde123}"}, + {"json", " "}, + {"json", ""}, + {"json", "\n"}, +}; +PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) +{ + const auto &[type, value] = GetParam(); + auto deserializers = makeDefaultDeserializersMap(); + + auto deserializer = deserializers.getDeserializerFor(type); + + EXPECT_TRUE(deserializer); + EXPECT_ANY_THROW(auto result = deserializer->deserialize(value)); +} diff --git a/Tests/Unit/EnvironmentVarsConfigurationTest.cpp b/Tests/Unit/EnvironmentVarsConfigurationTest.cpp new file mode 100644 index 0000000..df86075 --- /dev/null +++ b/Tests/Unit/EnvironmentVarsConfigurationTest.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" + + +#ifdef _WIN32 +#define _7BIT_CONF_PUT_ENV _putenv +#else +#define _7BIT_CONF_PUT_ENV putenv +#endif + +class EnvironmentVarsConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + EnvironmentVarsConfigurationTest() + { + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING=test"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_DOUBLE___double=1.4"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_BOOL___bool=true"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING_LIST__0=string"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING_LIST__1=string1"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_STRING_LIST__2=string2"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__0___int=1"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__1___int=3"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__2___int=22"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_JSON___json={\"key\": \"value\"}"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_OBJECT__INNER__OBJECT=string"); + _7BIT_CONF_PUT_ENV((char *)"7BIT_OTHER_CONFIG_TEST_STRING=string2"); + } + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(EnvironmentVarsConfigurationTest, ShouldFailCreationDueToNullParser) +{ + EXPECT_THROW(auto result = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_", nullptr), + sb::cf::NullPointerException); +} + +TEST_F(EnvironmentVarsConfigurationTest, ShouldFailProviderCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::EnvironmentVarsConfigurationProvider(nullptr), sb::cf::NullPointerException); +} + +TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVars) +{ + auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_")->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"TEST_STRING", "test"}, + {"TEST_DOUBLE", 1.4}, + {"TEST_BOOL", true}, + {"TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, + {"TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, + {"TEST_JSON", {{"key", "value"}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVarsWithPrefix) +{ + auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_")->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"CONFIG_TEST_STRING", "test"}, + {"CONFIG_TEST_DOUBLE", 1.4}, + {"CONFIG_TEST_BOOL", true}, + {"OTHER_CONFIG_TEST_STRING", "string2"}, + {"CONFIG_TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, + {"CONFIG_TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"CONFIG_TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, + {"CONFIG_TEST_JSON", {{"key", "value"}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(EnvironmentVarsConfigurationTest, ShouldNotLoadConfFromEnvVars) +{ + auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BITCONFIGURATION_")->build(mock); + + provider->load(); + + EXPECT_TRUE(provider->getConfiguration().empty()); +} diff --git a/Tests/Unit/InMemoryConfigurationTest.cpp b/Tests/Unit/InMemoryConfigurationTest.cpp new file mode 100644 index 0000000..2d4f9d2 --- /dev/null +++ b/Tests/Unit/InMemoryConfigurationTest.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/InMemoryConfiguration.hpp" + +class InMemoryConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + InMemoryConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(InMemoryConfigurationTest, ShouldFailProviderCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::InMemoryConfigurationProvider(nullptr), sb::cf::NullPointerException); +} + +TEST_F(InMemoryConfigurationTest, ShouldLoadSimpleSettingConfiguration) +{ + auto provider = + sb::cf::InMemoryConfigurationSource::create({{"yes:yes:inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}})->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"yes", {{"yes", {{"inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} diff --git a/Tests/Unit/JsonConfigurationTest.cpp b/Tests/Unit/JsonConfigurationTest.cpp new file mode 100644 index 0000000..b95bf8c --- /dev/null +++ b/Tests/Unit/JsonConfigurationTest.cpp @@ -0,0 +1,37 @@ +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/JsonConfiguration.hpp" + +class JsonConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + JsonConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(JsonConfigurationTest, ShouldFailProviderCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::JsonConfigurationProvider(nullptr), sb::cf::NullPointerException); +} + +TEST_F(JsonConfigurationTest, ShouldLoadSimpleJsonConfig) +{ + auto provider = sb::cf::JsonConfigurationSource::create({{"hello", 12345}})->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"hello", 12345}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} diff --git a/Tests/Unit/JsonFileConfigurationTest.cpp b/Tests/Unit/JsonFileConfigurationTest.cpp new file mode 100644 index 0000000..f02cca2 --- /dev/null +++ b/Tests/Unit/JsonFileConfigurationTest.cpp @@ -0,0 +1,62 @@ +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" + +class JsonFileConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + JsonFileConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(JsonFileConfigurationTest, ShouldFailProviderCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::JsonFileConfigurationProvider(nullptr), sb::cf::NullPointerException); +} + +TEST_F(JsonFileConfigurationTest, ShouldLoadSimpleJsonConfigFile) +{ + auto provider = sb::cf::JsonFileConfigurationSource::create("appsettings.json")->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, + {"MySetting", "appsettings.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(JsonFileConfigurationTest, ShouldNotLoadNonExistingJsonConfigFile) +{ + auto provider = sb::cf::JsonFileConfigurationSource::create("nonExisting.json", true)->build(mock); + + provider->load(); + + EXPECT_TRUE(provider->getConfiguration().empty()); +} + +TEST_F(JsonFileConfigurationTest, ShouldFailLoadingNonExistingJsonConfigFile) +{ + auto provider = sb::cf::JsonFileConfigurationSource ::create("nonExisting.json")->build(mock); + + EXPECT_THROW(provider->load(), sb::cf::ConfigFileNotFoundException); +} + +TEST_F(JsonFileConfigurationTest, ShouldFailLoadingBadJsonConfigFile) +{ + auto provider = sb::cf::JsonFileConfigurationSource ::create("bad.json")->build(mock); + + EXPECT_THROW(provider->load(), sb::cf::BadConfigFileException); +} diff --git a/Tests/Unit/JsonObjectExtTest.cpp b/Tests/Unit/JsonObjectExtTest.cpp new file mode 100644 index 0000000..e65eae1 --- /dev/null +++ b/Tests/Unit/JsonObjectExtTest.cpp @@ -0,0 +1,281 @@ +#include +#include + +#include "SevenBit/Conf/Details/JsonExt.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "Utilities/ParamsTest.hpp" + +using namespace sb::cf::json; + +class JsonObjectExtTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + JsonObjectExtTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +Params FindData{ + {"str", true, "hello"}, + {"number", true, 123}, + {"array", true, sb::cf::JsonArray{{{"key", "value"}}}}, + {"inner", true, -123}, + {"nonExisting", false, tao::json::null}, +}; +PARAMS_TEST(JsonObjectExtTest, ShouldFing, FindData) +{ + sb::cf::JsonValue json = { + {"str", "hello"}, {"number", 123}, {"array", sb::cf::JsonArray{{{"key", "value"}}}}, {"inner", -123}}; + + auto &[keys, expectedFound, expectedValue] = GetParam(); + auto valuePtr = sb::cf::details::JsonExt::find(json, keys); + EXPECT_EQ(!!valuePtr, expectedFound); + if (valuePtr) + { + EXPECT_EQ(*valuePtr, expectedValue); + } +} + +Params DeepFindData{ + {"inner:inner:str", true, "hello2"}, + {"inner:number", true, 1231}, + {"array:0:key", true, "value"}, + {"array:1:key", true, 12}, + {"array:2:key", false, tao::json::null}, + {"array:0.123:key", false, tao::json::null}, + {"array:0 123:key", false, tao::json::null}, + {"array:0 apok:key", false, tao::json::null}, + {"array:-1:key", false, tao::json::null}, + {"array:9notNumber:key", false, tao::json::null}, + {"inner:inner:nonExisting", false, tao::json::null}, + {"inner:inner:str:nonExisting", false, tao::json::null}, + {"nonExisting", false, tao::json::null}, + {"inner::inner:str", false, tao::json::null}, + +}; +PARAMS_TEST(JsonObjectExtTest, ShouldDeepFing, DeepFindData) +{ + sb::cf::JsonValue json = {{"str", "hello"}, + {"number", 123}, + {"array", sb::cf::JsonArray{{{"key", "value"}}, {{"key", 12}}}}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + { + {"str", "hello2"}, + {"number", 1232}, + }}}}}; + + auto &[keys, expectedFound, expectedValue] = GetParam(); + auto valuePtr = sb::cf::details::JsonExt::deepFind(json, keys); + EXPECT_EQ(!!valuePtr, expectedFound); + if (valuePtr) + { + EXPECT_EQ(*valuePtr, expectedValue); + } +} + +TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverride) +{ + sb::cf::JsonObject json = {{"str", "hello"}, + {"number", 123}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + { + {"str", "hello2"}, + {"number", 1232}, + }}}}}; + + sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:str") = "hello3"; + sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:inner:str") = "hello5"; + + sb::cf::JsonObject expectedJson = {{"str", "hello"}, + {"number", 123}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + {{"str", "hello3"}, + {"number", 1232}, + {"inner", + { + {"str", "hello5"}, + }}}}}}}; + + EXPECT_EQ(json, expectedJson); +} + +TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideArrayElement) +{ + sb::cf::JsonObject json = {{"str", "hello"}}; + + sb::cf::details::JsonExt::deepGetOrOverride(json, "array:3:object") = "value"; + + sb::cf::JsonObject expected = { + {"str", "hello"}, + {"array", + sb::cf::JsonArray{sb::cf::JsonValue{}, sb::cf::JsonValue{}, sb::cf::JsonValue{}, {{"object", "value"}}}}, + }; + + EXPECT_EQ(json, expected); +} + +TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideExistingArrayElement) +{ + sb::cf::JsonObject json = {{"str", "hello"}, {"array", sb::cf::JsonArray{1234, {{"second", "element"}}}}}; + + sb::cf::details::JsonExt::deepGetOrOverride(json, "array:3:object") = "value"; + + sb::cf::JsonObject expected = { + {"str", "hello"}, + {"array", sb::cf::JsonArray{1234, {{"second", "element"}}, sb::cf::JsonValue{}, {{"object", "value"}}}}, + }; + + EXPECT_EQ(json, expected); +} + +TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideWrongArrayElement) +{ + sb::cf::JsonObject json = {{"str", "hello"}}; + + sb::cf::details::JsonExt::deepGetOrOverride(json, "array:-3:object") = "value"; + + sb::cf::JsonObject expected = {{"str", "hello"}, {"array", {{"-3", {{"object", "value"}}}}}}; + + EXPECT_EQ(json, expected); +} + +TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideDestroy) +{ + sb::cf::JsonObject json = {{"str", "hello"}, + {"number", 123}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + { + {"str", "hello2"}, + {"number", 1232}, + }}}}}; + + sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:str:fail") = "value"; + + sb::cf::JsonObject expected = {{"str", "hello"}, + {"number", 123}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + { + {"str", {{"fail", "value"}}}, + {"number", 1232}, + }}}}}; + + EXPECT_EQ(json, expected); +} + +TEST_F(JsonObjectExtTest, ShouldFaildDeepGetOrOverrideEmpty) +{ + sb::cf::JsonObject json = {{"str", "hello"}}; + + EXPECT_ANY_THROW(sb::cf::details::JsonExt::deepGetOrOverride(json, std::vector{})); +} + +TEST_F(JsonObjectExtTest, SouldDeepMergeEmptyJsonValue) +{ + sb::cf::JsonValue json; + + sb::cf::JsonValue jsonOverride = {{"str", "helloOv"}}; + + sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); + + sb::cf::JsonValue expected = {{"str", "helloOv"}}; + + EXPECT_EQ(json, expected); +} + +TEST_F(JsonObjectExtTest, SouldDeepMergeJsonValue) +{ + + sb::cf::JsonValue json = {{"str", "hello"}, + {"number", 123}, + {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, + {"default", "default"}, + {"inner", + { + {"str", "hello1"}, + {"number", 1231}, + + }}}; + + sb::cf::JsonValue jsonOverride = {{"str", "helloOv"}, + {"default", sb::cf::json::uninitialized}, + {"array", sb::cf::JsonArray{3, {{"value", "value"}}, 1}}, + {"inner", + {{"number", 12313}, + {"inner", + { + {"str", "hello2Ov"}, + {"number", 12323}, + }}}}}; + + sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); + + sb::cf::JsonObject expectedJson = {{"str", "helloOv"}, + {"number", 123}, + {"array", sb::cf::JsonArray{3, {{"value", "value"}}, 1, 4, 5, 6}}, + {"default", "default"}, + {"inner", + {{"str", "hello1"}, + {"number", 12313}, + {"inner", + { + {"str", "hello2Ov"}, + {"number", 12323}, + }}}}}; + + EXPECT_EQ(json, expectedJson); +} + +TEST_F(JsonObjectExtTest, SouldDeepMergeJsonArray) +{ + + sb::cf::JsonArray json{{{"str", "hello"}}, + {{"number", 123}}, + sb::cf::JsonValue{}, + {{"inner", + { + {"str", "hello1"}, + {"number", 1231}, + + }}}}; + + sb::cf::JsonArray jsonOverride{ + {{"str", "hello22"}}, + sb::cf::JsonValue{}, + {{"number", 123}}, + }; + + sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); + + sb::cf::JsonArray expectedJson{{{"str", "hello22"}}, + {{"number", 123}}, + {{"number", 123}}, + {{"inner", + { + {"str", "hello1"}, + {"number", 1231}, + + }}}}; + + EXPECT_EQ(json, expectedJson); +} diff --git a/Tests/Unit/JsonStreamConfigurationTest.cpp b/Tests/Unit/JsonStreamConfigurationTest.cpp new file mode 100644 index 0000000..6234237 --- /dev/null +++ b/Tests/Unit/JsonStreamConfigurationTest.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/JsonStreamConfiguration.hpp" + +class JsonStreamConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + JsonStreamConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(JsonStreamConfigurationTest, ShouldFailProviderCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::JsonStreamConfigurationProvider(nullptr), sb::cf::NullPointerException); +} + +TEST_F(JsonStreamConfigurationTest, ShouldLoadConfigFromStream) +{ + std::stringstream stream; + + stream << "{\"hello\": 12345, \"string\": \"asdf\"}"; + + auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"hello", 12345}, {"string", "asdf"}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(JsonStreamConfigurationTest, ShouldFailLoadingConfigFromBadStream) +{ + std::stringstream stream; + + stream << "\"hello\""; + + auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); + + EXPECT_THROW(provider->load(), sb::cf::BadStreamException); +} + +TEST_F(JsonStreamConfigurationTest, ShouldFailLoadingDueToDoubleStreamRead) +{ + std::stringstream stream; + + stream << "{\"hello\": 12345, \"string\": \"asdf\"}"; + + auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); + + provider->load(); + + EXPECT_ANY_THROW(provider->load()); +} diff --git a/Tests/Unit/KeyPerFileConfigurationTest.cpp b/Tests/Unit/KeyPerFileConfigurationTest.cpp new file mode 100644 index 0000000..6fa0a15 --- /dev/null +++ b/Tests/Unit/KeyPerFileConfigurationTest.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp" + +class KeyPerFileConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + KeyPerFileConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(KeyPerFileConfigurationTest, ShouldFailLoadingNonExistingDirectory) +{ + auto source = sb::cf::KeyPerFileConfigurationSource::create("nonexisting"); + + EXPECT_ANY_THROW(source->build(mock)); +} + +TEST_F(KeyPerFileConfigurationTest, ShouldLoadNonExistingDirectory) +{ + auto provider = sb::cf::KeyPerFileConfigurationSource::create("nonexisting", true)->build(mock); + + provider->load(); + + EXPECT_TRUE(provider->getConfiguration().empty()); +} + +TEST_F(KeyPerFileConfigurationTest, ShouldLoadDirectoryConfig) +{ + auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory")->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"settingOne", + {{"number", 12345}, + {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, + {"string", "string"}, + {"object", {{"num", 134}, {"string", "string"}}}}}, + {"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConfigFiles) +{ + auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory", false, "settingOne")->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConditionConfigFiles) +{ + auto provider = + sb::cf::KeyPerFileConfigurationSource::create("Directory", false, [](const std::filesystem::path &path) { + return path.filename() == "settingOne.json"; + })->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} diff --git a/Tests/Unit/MapConfigutationTest.cpp b/Tests/Unit/MapConfigutationTest.cpp new file mode 100644 index 0000000..5f42e98 --- /dev/null +++ b/Tests/Unit/MapConfigutationTest.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources/MapConfiguration.hpp" + +class MapConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + MapConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(MapConfigurationTest, ShouldFailProviderCreationDueToNullSource) +{ + EXPECT_THROW(sb::cf::MapConfigurationProvider(nullptr, nullptr), sb::cf::NullPointerException); +} + +TEST_F(MapConfigurationTest, ShouldMapSimpleConfiguration) +{ + auto provider = sb::cf::MapConfigurationSource::create(sb::cf::JsonConfigurationSource::create({{"yes", 12345}}), + [](sb::cf::JsonObject ob) { + ob["yes2"] = "yes"; + return std::move(ob); + }) + ->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"yes", 12345}, {"yes2", "yes"}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} diff --git a/Tests/Unit/RunTests.cpp b/Tests/Unit/RunTests.cpp new file mode 100644 index 0000000..0bdcecc --- /dev/null +++ b/Tests/Unit/RunTests.cpp @@ -0,0 +1,10 @@ +#include + +int main(int argc, char *argv[]) +{ + // Run a specific test only + // testing::GTEST_FLAG(filter) = "Template.*"; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Tests/Unit/SettingParserBuilderTest.cpp b/Tests/Unit/SettingParserBuilderTest.cpp new file mode 100644 index 0000000..52a39dc --- /dev/null +++ b/Tests/Unit/SettingParserBuilderTest.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include "Mocks/DeserializerMock.hpp" +#include "Mocks/SettingSplitterMock.hpp" +#include "Mocks/ValueDeserializersMapMock.hpp" +#include "SevenBit/Conf/Details/SettingParser.hpp" +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +#include "SevenBit/Conf/SettingParserBuilder.hpp" + +class SettingParserBuilderTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + SettingParserBuilderTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(SettingParserBuilderTest, ShouldBuildDefault) +{ + sb::cf::SettingParserBuilder builder; + + auto parser = builder.build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_EQ(casted.getDefaultType(), "string"); + EXPECT_FALSE(casted.getAllowEmptyKeys()); + EXPECT_TRUE(casted.getThrowOnUnknownType()); +} + +TEST_F(SettingParserBuilderTest, ShouldUseCustomConfig) +{ + sb::cf::SettingParserBuilder builder; + + sb::cf::SettingParserConfig config; + config.defaultType = "int"; + config.throwOnUnknownType = false; + config.allowEmptyKeys = true; + auto parser = builder.useConfig(config).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_EQ(casted.getDefaultType(), "int"); + EXPECT_TRUE(casted.getAllowEmptyKeys()); + EXPECT_FALSE(casted.getThrowOnUnknownType()); +} + +TEST_F(SettingParserBuilderTest, ShouldUseValueDeserializer) +{ + sb::cf::SettingParserBuilder builder; + + auto parser = builder.useDefaultValueDeserializers() + .useValueDeserializer("newType", std::make_unique()) + .build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_EQ(casted.getDefaultType(), "string"); + EXPECT_FALSE(casted.getAllowEmptyKeys()); + EXPECT_TRUE(casted.getThrowOnUnknownType()); + EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("newType")); + EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("int")); + EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("bool")); +} + +TEST_F(SettingParserBuilderTest, ShouldUseCustomValueDeserializerMap) +{ + sb::cf::SettingParserBuilder builder; + + auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_EQ(casted.getDefaultType(), "string"); + EXPECT_FALSE(casted.getAllowEmptyKeys()); + EXPECT_TRUE(casted.getThrowOnUnknownType()); +} + +TEST_F(SettingParserBuilderTest, ShouldUseCustomSplitter) +{ + sb::cf::SettingParserBuilder builder; + + auto parser = builder.useSplitter(std::make_unique()).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_EQ(casted.getDefaultType(), "string"); + EXPECT_FALSE(casted.getAllowEmptyKeys()); + EXPECT_TRUE(casted.getThrowOnUnknownType()); +} diff --git a/Tests/Unit/SettingParserTest.cpp b/Tests/Unit/SettingParserTest.cpp new file mode 100644 index 0000000..c4eba41 --- /dev/null +++ b/Tests/Unit/SettingParserTest.cpp @@ -0,0 +1,182 @@ +#include +#include +#include + +#include "Mocks/DeserializerMock.hpp" +#include "Mocks/SettingSplitterMock.hpp" +#include "Mocks/ValueDeserializersMapMock.hpp" +#include "SevenBit/Conf/Details/SettingParser.hpp" +#include "SevenBit/Conf/Exceptions.hpp" + +class SettingParserTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + SettingParserTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(SettingParserTest, ShouldParseSetting) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--option:deep:deep!int=123"; + sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); + sb::cf::JsonValue returnedValue = 123; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; + + EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); + EXPECT_EQ(parser.getDefaultType(), "string"); + EXPECT_TRUE(parser.getThrowOnUnknownType()); + EXPECT_FALSE(parser.getAllowEmptyKeys()); +} + +TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullSplitter) +{ + sb::cf::ISettingSplitter::Ptr splitter; + auto deserializers = std::make_unique(); + + EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, true}), + sb::cf::ConfigException); +} + +TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullDeserializers) +{ + sb::cf::IValueDeserializersMap::Ptr deserializers; + auto splitter = std::make_unique(); + + EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, true}), + sb::cf::ConfigException); +} + +TEST_F(SettingParserTest, ShouldUseDefaultType) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--option:deep:deep=value"; + sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); + sb::cf::JsonValue returnedValue = "value"; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; + + EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); + EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); +} + +TEST_F(SettingParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--!string=value"; + sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); + sb::cf::JsonValue returnedValue = "value"; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; + + EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); + EXPECT_EQ(parser.getDefaultType(), "string"); + EXPECT_TRUE(parser.getThrowOnUnknownType()); + EXPECT_TRUE(parser.getAllowEmptyKeys()); +} + +TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueEmptyKey) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--!string=value"; + sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; + + EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); + EXPECT_EQ(parser.getDefaultType(), "string"); + EXPECT_TRUE(parser.getThrowOnUnknownType()); + EXPECT_FALSE(parser.getAllowEmptyKeys()); +} + +TEST_F(SettingParserTest, ShouldUseDefaultTypeForUnknownType) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--option:deep:deep!unknown=value"; + sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor) + .WillOnce(testing::Return(nullptr)) + .WillOnce(testing::Return(&deserializer)); + sb::cf::JsonValue returnedValue = "value"; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; + + EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); + EXPECT_EQ(parser.getDefaultType(), "string"); + EXPECT_FALSE(parser.getThrowOnUnknownType()); + EXPECT_TRUE(parser.getAllowEmptyKeys()); +} + +TEST_F(SettingParserTest, ShouldFailDueToForUnknownType) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--option:deep:deep!unknown=value"; + sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; + + EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); + EXPECT_EQ(parser.getDefaultType(), "string"); + EXPECT_TRUE(parser.getThrowOnUnknownType()); + EXPECT_TRUE(parser.getAllowEmptyKeys()); +} + +TEST_F(SettingParserTest, ShouldFailDueToForUnknownDefaultType) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::string_view setting = "--option:deep:deep!unknown=value"; + sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); + sb::cf::JsonValue returnedValue = "value"; + + sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; + + EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); + EXPECT_EQ(parser.getDefaultType(), "string"); + EXPECT_FALSE(parser.getThrowOnUnknownType()); + EXPECT_TRUE(parser.getAllowEmptyKeys()); +} diff --git a/Tests/Unit/SettingSplitterTest.cpp b/Tests/Unit/SettingSplitterTest.cpp new file mode 100644 index 0000000..be5a07a --- /dev/null +++ b/Tests/Unit/SettingSplitterTest.cpp @@ -0,0 +1,114 @@ +#include +#include + +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/Utils.hpp" +#include "Utilities/ParamsTest.hpp" + + +class SettingSplitterTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + SettingSplitterTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +static Params SplitSettingData = { + {"--", {{""}}}, + {"one", {{"one"}}}, + {"--====", {{""}, std::nullopt, "==="}}, + {"--option!!type=val", {{"option!"}, "type", "val"}}, + {"--option!type=value", {{"option"}, "type", "value"}}, + {"//option___type;value", {{"option"}, "type", "value"}}, + {"--option=value", {{"option"}, std::nullopt, "value"}}, + {"--option=", {{"option"}, std::nullopt, ""}}, + {"--option!!type=", {{"option!"}, "type", ""}}, + {"--option!!type", {{"option!"}, "type"}}, + {"--option!!=type=val", {{"option!"}, "", "type=val"}}, + {"--option!!:inner!=type=val", {{"option!!", "inner"}, "", "type=val"}}, + {"--option!!:inner:::!=type=val", {{"option!!", "inner", "", "", ""}, "", "type=val"}}, + {":option:inner=type=val", {{"", "option", "inner"}, std::nullopt, "type=val"}}, + {"!!option:inner=type=val", {{"!"}, "option:inner", "type=val"}}, + {":option:inner=value", {{"", "option", "inner"}, std::nullopt, "value"}}, + {":option:inner=value::!", {{"", "option", "inner"}, std::nullopt, "value::!"}}, + {":option!!:inner=value::!", {{"", "option!"}, ":inner", "value::!"}}, + {":::!=value::!", {{"", "", "", ""}, "", "value::!"}}, + {"::!:=value::!", {{"", "", ""}, ":", "value::!"}}, + {":=:!:=value::!", {{"", ""}, std::nullopt, ":!:=value::!"}}, + {"__=:!___=value::!", {{"", ""}, std::nullopt, ":!___=value::!"}}, + {"_______=value", {{"", "", ""}, "", "value"}}, + {"__hello_____type=value", {{"", "hello", ""}, "type", "value"}}, + {"__hello__type=value", {{"", "hello", "type"}, std::nullopt, "value"}}, +}; +PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) +{ + const auto &[setting, expected] = GetParam(); + sb::cf::details::SettingSplitter splitter{{"--", "//"}, {"=", ";"}, {"!", "___"}, {":", "__"}}; + + EXPECT_EQ(splitter.split(setting), expected); +} + +static OneParams SettingSplittersData = {"=", ">", "===", "<<<<", "////"}; + +static OneParams SettingPrefixesData = {":", ";", "%%$", "***", "++"}; + +static OneParams KeySplittersData = {":", ";", "__", "{}", "\\"}; + +static OneParams TypeMarkersData = {"!", "@", ":", "[]", "{}"}; + +static Params, std::string, std::string> SplitSettingSimpleData = { + {{"key1", "key2", "key3"}, "string", "value"}, + {{"key1"}, "string", "value"}, + {{""}, "", ""}, + {{""}, "type", ""}, + {{"key"}, "", ""}, + {{""}, "", "value"}, +}; +PARAMS_TEST_COMBINED_5(SettingSplitterTest, ShouldSplitWithDifferentSplitters, SettingPrefixesData, + SettingSplittersData, TypeMarkersData, KeySplittersData, SplitSettingSimpleData) +{ + const auto &[prefix, settingSplitter, typeMarker, keySplitter, setting] = GetParam(); + const auto &[keys, type, value] = setting; + + sb::cf::details::SettingSplitter splitter{{prefix}, {settingSplitter}, {typeMarker}, {keySplitter}}; + + auto key = sb::cf::details::utils::joinViews(keys, keySplitter); + auto fullSetting = prefix + key + typeMarker + type + settingSplitter + value; + EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); +} + +static Params, std::vector, std::vector, + std::vector, sb::cf::ISettingSplitter::Result> + EmptySplittersData = { + {{}, {"=", ";"}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int", "value"}}, + {{"--", "//"}, {}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, + {{"--", "//"}, {"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, + {{"--", "//"}, {"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, + {{}, {}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int=value"}}, + {{}, {"=", ";"}, {}, {":", "__"}, {{"--key", "deep!int"}, std::nullopt, "value"}}, + {{}, {"=", ";"}, {"!", "___"}, {}, {{"--key:deep"}, "int", "value"}}, + {{"--", "//"}, {}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, + {{"--", "//"}, {}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, + {{"--", "//"}, {"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, + {{}, {}, {}, {":", "__"}, {{"--key", "deep!int=value"}}}, + {{}, {}, {"!", "___"}, {}, {{"--key:deep"}, "int=value"}}, + {{}, {"=", ";"}, {}, {}, {{"--key:deep!int"}, std::nullopt, "value"}}, + {{"--", "//"}, {}, {}, {}, {{"key:deep!int=value"}}}, + {{}, {}, {}, {}, {{"--key:deep!int=value"}}}, + +}; +PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersData) +{ + const auto &[prefixes, settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); + + sb::cf::details::SettingSplitter splitter{prefixes, settingSplitters, typeMarkers, keySplitters}; + + EXPECT_EQ(splitter.split("--key:deep!int=value"), expected); +} diff --git a/Tests/Unit/TestTemplate.cpp b/Tests/Unit/TestTemplate.cpp new file mode 100644 index 0000000..a6fc38a --- /dev/null +++ b/Tests/Unit/TestTemplate.cpp @@ -0,0 +1,20 @@ +#include +#include + +class Template : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + Template() {} + + void SetUp() override {} + + void TearDown() override {} + + ~Template() {} + + static void TearDownTestSuite() {} +}; + +TEST_F(Template, ExampleTest) {} diff --git a/Tests/Unit/UtilsTest.cpp b/Tests/Unit/UtilsTest.cpp new file mode 100644 index 0000000..6cfc816 --- /dev/null +++ b/Tests/Unit/UtilsTest.cpp @@ -0,0 +1,327 @@ +#include +#include + +#include "SevenBit/Conf/Details/Utils.hpp" +#include "Utilities/ParamsTest.hpp" + +class UtilsTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + UtilsTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +Params CheckNumberStringsData{ + {"123", true}, {"1", true}, {"0912837545234123", true}, {"asd", false}, + {"", false}, {"alk1", false}, {"1223-", false}, {"1223#", false}, + {"1223+=", false}, {"1223.123", false}, {"1223.123", false}, +}; +PARAMS_TEST(UtilsTest, ShouldCheckNumberStrings, CheckNumberStringsData) +{ + auto &[string, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::isNumberString(string), expected); +} + +Params IgnoreCaseLessData{ + {"", "", false}, + {"I", "i", false}, + {"i", "I", false}, + {"123@#", "123@#", false}, + {"abcdef", "ABCDEF", false}, + {"abcDEF", "abcdef", false}, + {"abcDEF", "ABCdef", false}, + {"abcDEF12##", "abCdef12##", false}, + {"", "abcded", true}, + {"BA", "ab", false}, + {"ab", "ab\n", true}, + {"1222", "12", false}, +}; +PARAMS_TEST(UtilsTest, ShouldIgnoreCaseLessCompareStrings, IgnoreCaseLessData) +{ + auto &[string, search, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::ignoreCaseLess(string, search), expected); +} + +Params IgnoreCaseEqualsData{ + {"", "", true}, + {"I", "i", true}, + {"i", "I", true}, + {"123@#", "123@#", true}, + {"abcdef", "ABCDEF", true}, + {"abcDEF", "abcdef", true}, + {"abcDEF", "ABCdef", true}, + {"abcDEF12##", "abCdef12##", true}, + {"", "abcded", false}, + {"BA", "ab", false}, + {"ab", "ab\n", false}, + {"1222", "12", false}, +}; +PARAMS_TEST(UtilsTest, ShouldIgnoreCaseCompareStrings, IgnoreCaseEqualsData) +{ + auto &[string, search, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::ignoreCaseEqual(string, search), expected); +} + +Params ContainsAtData{ + {"", 0, "", false}, {"hello", 0, "", false}, {"", 12, "", false}, + {"i", 0, "i", true}, {"index", 0, "in", true}, {"index", 3, "in", false}, + {"index", 30, "in", false}, {"index", 2, "dex", true}, {"index", 2, "dexx", false}, + {"index", 2, "dexx", false}, {"index", 3, "dex", false}, {"123@#", 0, "123@#", true}, + {"123@#", 2, "3", true}, {"123@#", 2, "@", false}, {"abcdef", 0, "abcdef", true}, + {"", 0, "abcded", false}, {"BA", 0, "ab", false}, {"ab", 0, "ab\n", false}, + {"1222", 0, "12", true}, +}; +PARAMS_TEST(UtilsTest, ShouldContainsAt, ContainsAtData) +{ + auto &[string, index, search, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::containsAt(string, index, search), expected); +} + +Params, std::optional> ContainsAtMultitData{ + {"", 0, {""}, std::nullopt}, + {"", 12, {""}, std::nullopt}, + {"i", 0, {"i"}, "i"}, + {"index", 0, {"none", "in"}, "in"}, + {"index", 0, {"oh", "bob", "in"}, "in"}, + {"index", 30, {"in"}, std::nullopt}, + {"index", 2, {"yes", "dex"}, "dex"}, + {"index", 2, {"dexx"}, std::nullopt}, + {"index", 2, {"dexx"}, std::nullopt}, + {"index", 3, {"dex"}, std::nullopt}, + {"123@#", 0, {"123@#"}, "123@#"}, + {"123@#", 2, {"3"}, "3"}, + {"123@#", 2, {"@"}, std::nullopt}, + {"abcdef", 0, {"abcdef"}, "abcdef"}, + {"", 0, {"abcded"}, std::nullopt}, + {"BA", 0, {"ab"}, std::nullopt}, + {"ab", 0, {"ab\n"}, std::nullopt}, + {"1222", 0, {"12"}, "12"}, +}; +PARAMS_TEST(UtilsTest, ShouldContainsAtMulti, ContainsAtMultitData) +{ + auto &[string, index, searches, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::containsAt(string, index, searches), expected); +} + +Params ContainsAtFromEndData{ + {"", 0, "", false}, {"", 12, "", false}, {"i", 0, "i", true}, + {"index", 1, "in", true}, {"index", 4, "in", false}, {"index", 30, "in", false}, + {"index", 4, "dex", true}, {"index", 2, "dexx", false}, {"index", 2, "dexx", false}, + {"index", 2, "dex", false}, {"123@#", 4, "123@#", true}, {"123@#", 2, "3", true}, + {"123@#", 2, "@", false}, {"abcdef", 5, "abcdef", true}, {"", 0, "abcded", false}, + {"BA", 2, "ab", false}, {"ab", 2, "ab\n", false}, {"1222", 1, "12", true}, +}; +PARAMS_TEST(UtilsTest, ShouldContainsAtFromEnd, ContainsAtFromEndData) +{ + auto &[string, index, search, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::containsAtFromEnd(string, index, search), expected); +} + +Params, std::optional> + ContainsAtFromEndMultitData{ + {"", 0, {}, std::nullopt}, + {"hello", 0, {}, std::nullopt}, + {"", 0, {""}, std::nullopt}, + {"", 12, {""}, std::nullopt}, + {"i", 0, {"i"}, "i"}, + {"index", 1, {"none", "in"}, "in"}, + {"index", 1, {"oh", "bob", "in"}, "in"}, + {"index", 30, {"in"}, std::nullopt}, + {"index", 4, {"yes", "dex"}, "dex"}, + {"index", 2, {"dexx"}, std::nullopt}, + {"index", 2, {"dexx"}, std::nullopt}, + {"index", 3, {"dex"}, std::nullopt}, + {"123@#", 4, {"123@#"}, "123@#"}, + {"123@#", 2, {"3"}, "3"}, + {"123@#", 2, {"@"}, std::nullopt}, + {"abcdef", 5, {"abcdef"}, "abcdef"}, + {"", 0, {"abcded"}, std::nullopt}, + {"BA", 0, {"ab"}, std::nullopt}, + {"ab", 0, {"ab\n"}, std::nullopt}, + {"1222", 1, {"12"}, "12"}, + }; +PARAMS_TEST(UtilsTest, ShouldContainsAtFromEndMulti, ContainsAtFromEndMultitData) +{ + auto &[string, index, searches, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::containsAtFromEnd(string, index, searches), expected); +} + +Params StartsWithData{ + {"", "", true}, {"1234567", "123", true}, {"1234567", "", true}, + {"1234567", "1", true}, {"123", "123", true}, {"123", "1234", false}, + {"123", "890", false}, {"123", "1245", false}, {"1234567", "234", false}, + {"", "234", false}, {"123", "234", false}, +}; +PARAMS_TEST(UtilsTest, ShouldStartsWith, StartsWithData) +{ + auto &[string, search, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::startsWith(string, search), expected); +} + +Params> SplitStrData{ + {"", "/", {""}}, + {"", "", {""}}, + {"abcd", "", {"abcd"}}, + {"first:sec:for:5:6", "/", {"first:sec:for:5:6"}}, + {"first:sec:for:5:6", ":", {"first", "sec", "for", "5", "6"}}, + {"first123++/?sec123++/?for123++/?5123++/?6", "123++/?", {"first", "sec", "for", "5", "6"}}, + {":first:sec:for:5:6", ":", {"", "first", "sec", "for", "5", "6"}}, + {"first::sec:for:5:6", ":", {"first", "", "sec", "for", "5", "6"}}, + {"first:sec:for:5:6:", ":", {"first", "sec", "for", "5", "6", ""}}, + {"first__sec__for__5__6__", "__", {"first", "sec", "for", "5", "6", ""}}, + {":::", ":", {"", "", "", ""}}, +}; +PARAMS_TEST(UtilsTest, ShouldSplitString, SplitStrData) +{ + auto &[string, delim, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::split(string, delim), expected); +} + +Params, std::vector> SplitStrMultiData{ + {"", {}, {""}}, + {"hello", {}, {"hello"}}, + {"", {"/", "|"}, {""}}, + {"", {"", "|"}, {""}}, + {"abcd", {""}, {"abcd"}}, + {"ab:cd", {"", ":"}, {"ab", "cd"}}, + {"first:sec__for:5||6", {":", "__", "||"}, {"first", "sec", "for", "5", "6"}}, + {"first:sec__for:5||6", {":", "||"}, {"first", "sec__for", "5", "6"}}, + {"first:sec__for:5||6", {"???"}, {"first:sec__for:5||6"}}, + {"first:sec:for:5:6", {":"}, {"first", "sec", "for", "5", "6"}}, + {"first123++/?sec123++/?for123++/?5123++/?6", {"123++/?"}, {"first", "sec", "for", "5", "6"}}, + {":first:sec:for:5:6", {":"}, {"", "first", "sec", "for", "5", "6"}}, + {"first::sec:for:5:6", {":"}, {"first", "", "sec", "for", "5", "6"}}, + {"first:sec:for:5:6:", {":"}, {"first", "sec", "for", "5", "6", ""}}, + {":::", {":"}, {"", "", "", ""}}, +}; +PARAMS_TEST(UtilsTest, ShouldSplitStringMulti, SplitStrMultiData) +{ + auto &[string, delims, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::split(string, delims), expected); +} + +Params, std::optional>> + BreakStrData{ + {"", {""}, std::nullopt}, + {"", {}, std::nullopt}, + {"hello", {}, std::nullopt}, + {"key=value", {"="}, std::pair{"key", "value"}}, + {"key====value", {"===="}, std::pair{"key", "value"}}, + {"=keyvalue", {"="}, std::pair{"", "keyvalue"}}, + {"keyvalue=", {"="}, std::pair{"keyvalue", ""}}, + {"key=value", {"-"}, std::nullopt}, + {"abcd", {""}, std::nullopt}, + {"first=sec:for:5:6", {"/"}, std::nullopt}, + {"first:sec:for:5:6", {":"}, std::pair{"first", "sec:for:5:6"}}, + {"first__sec__for__5__6__", {"__"}, std::pair{"first", "sec__for__5__6__"}}, + }; +PARAMS_TEST(UtilsTest, ShouldBreakString, BreakStrData) +{ + auto &[string, delim, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::tryBreak(string, delim), expected); +} + +Params, std::optional>> + BreakFromEndStrData{ + {"", {""}, std::nullopt}, + {"", {}, std::nullopt}, + {"hello", {}, std::nullopt}, + {"key=value", {"="}, std::pair{"key", "value"}}, + {"key====value", {"===="}, std::pair{"key", "value"}}, + {"=keyvalue", {"="}, std::pair{"", "keyvalue"}}, + {"keyvalue=", {"="}, std::pair{"keyvalue", ""}}, + {"key=value", {"-"}, std::nullopt}, + {"abcd", {""}, std::nullopt}, + {"first=sec:for:5:6", {"/"}, std::nullopt}, + {"first:sec:for:5:6", {":"}, std::pair{"first:sec:for:5", "6"}}, + {"first__sec__for__5__6", {"__"}, std::pair{"first__sec__for__5", "6"}}, + }; +PARAMS_TEST(UtilsTest, ShouldBreakFromEndString, BreakFromEndStrData) +{ + auto &[string, delim, expected] = GetParam(); + auto res = sb::cf::details::utils::tryBreakFromEnd(string, delim); + EXPECT_EQ(sb::cf::details::utils::tryBreakFromEnd(string, delim), expected); +} + +Params, std::string, std::string> JoinStrData{ + {{""}, "/", ""}, + {{"first:sec:for:5:6"}, "/", "first:sec:for:5:6"}, + {{"first", "sec", "for", "5", "6"}, ":", "first:sec:for:5:6"}, + {{"first", "sec", "for", "5", "6"}, "123++/?", "first123++/?sec123++/?for123++/?5123++/?6"}, + {{"", "first", "sec", "for", "5", "6"}, ":", ":first:sec:for:5:6"}, + {{"first", "", "sec", "for", "5", "6"}, ":", "first::sec:for:5:6"}, + {{"first", "sec", "for", "5", "6", ""}, ":", "first:sec:for:5:6:"}, + {{"first", "sec:for:5:6:"}, ":", "first:sec:for:5:6:"}, + {{"first", "sec", "for:5:6:"}, ":", "first:sec:for:5:6:"}, + {{"first", "sec", "for", "5", "6"}, ":", "first:sec:for:5:6"}, +}; +PARAMS_TEST(UtilsTest, ShouldJoinStrings, JoinStrData) +{ + auto &[strings, delim, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::utils::joinViews(strings, delim), expected); +} + +TEST_F(UtilsTest, ShouldAssertPtr) +{ + int i = 1; + EXPECT_NO_THROW(sb::cf::details::utils::assertPtr(&i)); + EXPECT_THROW(sb::cf::details::utils::assertPtr(nullptr), sb::cf::NullPointerException); +} + +Params> ConvertToNumberIntData{ + {"123", true, {true, 123}}, {"-123", true, {true, -123}}, {"00123", true, {true, 123}}, + {"123 23", false, {true, 123}}, {"123.23", false, {true, 123}}, {"123adawadwa", false, {true, 123}}, +}; +PARAMS_TEST(UtilsTest, ShouldConvertToIntNumber, ConvertToNumberIntData) +{ + auto &[string, full, expected] = GetParam(); + auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); + + EXPECT_EQ(success, expected.first); + if (success) + { + EXPECT_EQ(result, expected.second); + } +} + +Params> ConvertToNumberDoubleData{ + {"123.123", true, {true, 123.123}}, {" 123.123", true, {true, 123.123}}, {"-123.22", true, {true, -123.22}}, + {"00123.2", true, {true, 123.2}}, {" 123.23asdw", false, {true, 123.23}}, {"123.23", false, {true, 123.23}}, + {"123.1adawadwa", false, {true, 123.1}}, +}; +PARAMS_TEST(UtilsTest, ShouldConvertToDoubleNumber, ConvertToNumberDoubleData) +{ + auto &[string, full, expected] = GetParam(); + auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); + + EXPECT_EQ(success, expected.first); + if (success) + { + EXPECT_EQ(result, expected.second); + } +} + +Params> ConvertToBoolData{ + {"true", true, {true, true}}, {"false", true, {true, false}}, {"-123.22", true, {false, false}}, + {"00123.2", true, {false, false}}, {"0", true, {true, false}}, {"0", true, {true, false}}, + {"0 asdad", false, {true, false}}, {"12 asdad", false, {true, true}}, {" 12", true, {true, true}}, + {"ttrue", true, {false, true}}, +}; +PARAMS_TEST(UtilsTest, ShouldConvertToBool, ConvertToBoolData) +{ + auto &[string, full, expected] = GetParam(); + auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); + + EXPECT_EQ(success, expected.first); + if (success) + { + EXPECT_EQ(result, expected.second); + } +} diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index 40bae2c..0000000 --- a/conanfile.txt +++ /dev/null @@ -1,9 +0,0 @@ -[requires] -gtest/1.14.0 -taocpp-json/1.0.0-beta.14 - -[generators] -CMakeDeps -CMakeToolchain -[layout] -cmake_layout From bbf3123bc89243182df613be06471878201b5630 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Tue, 20 Feb 2024 18:20:38 +0100 Subject: [PATCH 04/31] refactor split parser builders --- .../Conf/CommandLineParserBuilder.hpp | 53 ++ .../SevenBit/Conf/CommandLineParserConfig.hpp | 20 + .../Conf/Details/DefaultDeserializers.hpp | 23 + .../Conf/Details/Impl/SettingParser.hpp | 52 +- .../Conf/Details/Impl/SettingSplitter.hpp | 57 +-- .../Conf/Details/Impl/SettingsParser.hpp | 80 +++ .../Details/Impl/ValueDeserializersMap.hpp | 49 +- .../SevenBit/Conf/Details/SettingParser.hpp | 19 +- .../{ => Details}/SettingParserConfig.hpp | 8 +- .../SevenBit/Conf/Details/SettingSplitter.hpp | 15 +- .../SevenBit/Conf/Details/SettingsParser.hpp | 48 ++ Include/SevenBit/Conf/Details/Utils.hpp | 39 +- .../Conf/Details/ValueDeserializersMap.hpp | 24 +- .../SevenBit/Conf/IConfigurationBuilder.hpp | 19 +- Include/SevenBit/Conf/IDeserializer.hpp | 1 - Include/SevenBit/Conf/ISettingsParser.hpp | 21 + .../SevenBit/Conf/IValueDeserializersMap.hpp | 2 +- .../Conf/Impl/CommandLineParserBuilder.hpp | 98 ++++ .../Conf/Impl/SettingParserBuilder.hpp | 22 +- .../SevenBit/Conf/SettingParserBuilder.hpp | 4 +- .../Conf/Sources/CommandLineConfiguration.hpp | 12 +- .../Sources/Impl/CommandLineConfiguration.hpp | 16 +- Source/Source.cpp | 24 +- Tests/Unit/CommandLineConfigurationTest.cpp | 186 +++---- Tests/Unit/DeserializersTest.cpp | 474 +++++++++--------- Tests/Unit/SettingParserBuilderTest.cpp | 216 ++++---- Tests/Unit/SettingParserTest.cpp | 366 +++++++------- Tests/Unit/SettingSplitterTest.cpp | 228 ++++----- 28 files changed, 1264 insertions(+), 912 deletions(-) create mode 100644 Include/SevenBit/Conf/CommandLineParserBuilder.hpp create mode 100644 Include/SevenBit/Conf/CommandLineParserConfig.hpp create mode 100644 Include/SevenBit/Conf/Details/DefaultDeserializers.hpp create mode 100644 Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp rename Include/SevenBit/Conf/{ => Details}/SettingParserConfig.hpp (54%) create mode 100644 Include/SevenBit/Conf/Details/SettingsParser.hpp create mode 100644 Include/SevenBit/Conf/ISettingsParser.hpp create mode 100644 Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp diff --git a/Include/SevenBit/Conf/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/CommandLineParserBuilder.hpp new file mode 100644 index 0000000..a190bdc --- /dev/null +++ b/Include/SevenBit/Conf/CommandLineParserBuilder.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/CommandLineParserConfig.hpp" +#include "SevenBit/Conf/ISettingSplitter.hpp" +#include "SevenBit/Conf/ISettingsParser.hpp" +#include "SevenBit/Conf/IValueDeserializersMap.hpp" + +namespace sb::cf +{ + + class EXPORT CommandLineParserBuilder + { + private: + ISettingSplitter::Ptr _splitter; + IValueDeserializersMap::Ptr _valueDeserializersMap; + std::vector> _valueDeserializers; + std::optional _config; + + public: + CommandLineParserBuilder &useSplitter(ISettingSplitter::Ptr splitter); + + CommandLineParserBuilder &useValueDeserializersMap(IValueDeserializersMap::Ptr valueDeserializersMap); + + CommandLineParserBuilder &useValueDeserializer(std::string_view type, IDeserializer::Ptr valueDeserializer); + + CommandLineParserBuilder &useConfig(CommandLineParserConfig config); + + CommandLineParserBuilder &useDefaultValueDeserializers(); + + ISettingsParser::Ptr build(); + + private: + bool tryRemoveWhiteSpaceSplitters(); + + ISettingSplitter::Ptr getSplitter(); + + IValueDeserializersMap::Ptr getValueDeserializersMap(); + + CommandLineParserConfig &getConfig(); + }; + +} // namespace sb::cf + +#ifdef _7BIT_CONF_ADD_IMPL +#include "SevenBit/Conf/Impl/CommandLineParserBuilder.hpp" +#endif diff --git a/Include/SevenBit/Conf/CommandLineParserConfig.hpp b/Include/SevenBit/Conf/CommandLineParserConfig.hpp new file mode 100644 index 0000000..66c9ad0 --- /dev/null +++ b/Include/SevenBit/Conf/CommandLineParserConfig.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +namespace sb::cf +{ + struct CommandLineParserConfig + { + std::vector optionPrefixes = {"--", "/"}; + std::vector optionSplitters = {"=", " "}; + std::vector optionKeySplitters = {":"}; + std::vector optionTypeMarkers = {"!"}; + std::string_view defaultType = "string"; + bool throwOnUnknownType = true; + bool allowEmptyKeys = false; + }; +} // namespace sb::cf diff --git a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp new file mode 100644 index 0000000..74c8696 --- /dev/null +++ b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "SevenBit/Conf/Details/Deserializers.hpp" +#include "SevenBit/Conf/Details/SettingParser.hpp" +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/SettingsParser.hpp" + +namespace sb::cf +{ + struct DefaultDeserializers + { + static void addDefault(std::vector> &deserializers) + { + deserializers.emplace_back("string", std::make_unique()); + deserializers.emplace_back("bool", std::make_unique()); + deserializers.emplace_back("int", std::make_unique()); + deserializers.emplace_back("double", std::make_unique()); + deserializers.emplace_back("uint", std::make_unique()); + deserializers.emplace_back("json", std::make_unique()); + deserializers.emplace_back("null", std::make_unique()); + } + }; +} // namespace sb::cf diff --git a/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp b/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp index 84011b6..a5d03de 100644 --- a/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp +++ b/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp @@ -9,13 +9,11 @@ namespace sb::cf::details { INLINE SettingParser::SettingParser(ISettingSplitter::Ptr settingSplitter, - IValueDeserializersMap::Ptr valueDeserializersMap, std::string_view defaultType, - bool allowEmptyKeys, bool throwOnUnknownType) - : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)), - _defaultType(defaultType), _allowEmptyKeys(allowEmptyKeys), _throwOnUnknownType(throwOnUnknownType) + IValueDeserializersMap::Ptr valueDeserializersMap) + : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)) { - details::utils::assertPtr(_settingSplitter); - details::utils::assertPtr(_valueDeserializersMap); + utils::assertPtr(_settingSplitter); + utils::assertPtr(_valueDeserializersMap); } INLINE ISettingParser::Result SettingParser::parse(std::string_view setting) const @@ -23,10 +21,7 @@ namespace sb::cf::details try { auto [keys, type, value] = getSettingSplitter().split(setting); - - checkKeys(keys); - - return {std::move(keys), getDeserializerFor(type ? *type : getDefaultType()).deserialize(value)}; + return {std::move(keys), getValueDeserializersMap().getDeserializerFor(type).deserialize(value)}; } catch (const std::exception &e) { @@ -34,47 +29,10 @@ namespace sb::cf::details } } - INLINE const IDeserializer &SettingParser::getDeserializerFor(std::string_view type) const - { - auto deserializer = getValueDeserializersMap().getDeserializerFor(type); - if (!deserializer) - { - if (getThrowOnUnknownType()) - { - throw ConfigException("Unknown setting type: " + std::string{type}); - } - deserializer = getValueDeserializersMap().getDeserializerFor(getDefaultType()); - if (!deserializer) - { - throw ConfigException("Unknown default setting type: " + std::string{getDefaultType()}); - } - } - return *deserializer; - } - - INLINE void SettingParser::checkKeys(const std::vector &keys) const - { - if (getAllowEmptyKeys()) - { - return; - } - if (keys.empty() || std::any_of(keys.begin(), keys.end(), [](auto key) { return key.empty(); })) - { - throw ConfigException("Setting key cannot be empty"); - } - } - INLINE const ISettingSplitter &SettingParser::getSettingSplitter() const { return *_settingSplitter; } INLINE const IValueDeserializersMap &SettingParser::getValueDeserializersMap() const { return *_valueDeserializersMap; } - - INLINE std::string_view SettingParser::getDefaultType() const { return _defaultType; } - - INLINE bool SettingParser::getAllowEmptyKeys() const { return _allowEmptyKeys; } - - INLINE bool SettingParser::getThrowOnUnknownType() const { return _throwOnUnknownType; } - } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp index 469a458..6e397ee 100644 --- a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp @@ -6,37 +6,23 @@ namespace sb::cf::details { - INLINE SettingSplitter::SettingSplitter(std::vector settingPrefixes, - std::vector settingSplitters, + INLINE SettingSplitter::SettingSplitter(std::vector settingSplitters, std::vector typeMarkers, - std::vector keySplitters) - : _settingPrefixes(std::move(settingPrefixes)), _settingSplitters(std::move(settingSplitters)), - _typeMarkers(std::move(typeMarkers)), _keySplitters(std::move(keySplitters)) + std::vector keySplitters, const bool allowEmptyKeys) + : _settingSplitters(std::move(settingSplitters)), _typeMarkers(std::move(typeMarkers)), + _keySplitters(std::move(keySplitters)), _allowEmptyKeys(allowEmptyKeys) { } - INLINE ISettingSplitter::Result SettingSplitter::split(std::string_view setting) const + INLINE ISettingSplitter::Result SettingSplitter::split(const std::string_view setting) const { - auto [key, value] = splitSetting(tryRemovePrefix(setting)); - auto [rawKey, type] = splitType(key); + auto [rawKey, value] = splitSetting(setting); + auto type = tryExtractType(rawKey); return {splitKey(rawKey), type, value}; } - INLINE std::string_view SettingSplitter::tryRemovePrefix(std::string_view setting) const - { - for (auto &prefix : _settingPrefixes) - { - if (details::utils::startsWith(setting, prefix)) - { - setting.remove_prefix(prefix.size()); - break; - } - } - return setting; - } - INLINE std::pair> SettingSplitter::splitSetting( - std::string_view setting) const + const std::string_view setting) const { if (auto breakResult = details::utils::tryBreak(setting, _settingSplitters)) { @@ -45,18 +31,33 @@ namespace sb::cf::details return {setting, std::nullopt}; } - INLINE std::pair> SettingSplitter::splitType( - std::string_view key) const + INLINE std::optional SettingSplitter::tryExtractType(std::string_view &key) const { if (auto breakResult = details::utils::tryBreakFromEnd(key, _typeMarkers)) { - return {breakResult->first, breakResult->second}; + key = breakResult->first; + return breakResult->second; } - return {key, std::nullopt}; + return std::nullopt; } - INLINE std::vector SettingSplitter::splitKey(std::string_view key) const + INLINE std::vector SettingSplitter::splitKey(const std::string_view key) const { - return details::utils::split(key, _keySplitters); + auto keys = details::utils::split(key, _keySplitters); + checkKeys(keys); + return keys; + } + + INLINE void SettingSplitter::checkKeys(const std::vector &keys) const + { + if (_allowEmptyKeys) + { + return; + } + if (keys.empty() || + std::any_of(keys.begin(), keys.end(), [](const std::string_view &key) { return key.empty(); })) + { + throw ConfigException("Setting key cannot be empty"); + } } } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp b/Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp new file mode 100644 index 0000000..cd22d4b --- /dev/null +++ b/Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include "SevenBit/Conf/Details/SettingsParser.hpp" +#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Exceptions.hpp" + +#include + +namespace sb::cf::details +{ + INLINE SettingsParser::SettingsParser(ISettingSplitter::Ptr settingSplitter, + IValueDeserializersMap::Ptr valueDeserializersMap, + std::vector settingPrefixes, bool combinedSetting) + : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)), + _settingPrefixes(std::move(settingPrefixes)), _combinedSetting(combinedSetting) + { + utils::assertPtr(_settingSplitter); + utils::assertPtr(_valueDeserializersMap); + } + + INLINE JsonObject SettingsParser::parse(const std::vector &settings) const + { + JsonObject result; + for (size_t index = 0; index < settings.size(); ++index) + { + try + { + auto [keys, value] = parseArgument(settings, index); + JsonExt::updateWith(result, keys, std::move(value)); + } + catch (const std::exception &e) + { + throw SettingParserException("Parsing error for setting '" + std::string{settings[index]} + + "' error: " + e.what()); + } + } + return result; + } + + INLINE SettingsParser::ArgumentParseResult SettingsParser::parseArgument( + const std::vector &settings, size_t &index) const + { + auto setting = settings[index]; + const auto hasOptionPrefix = tryRemovePrefix(setting); + + auto [keys, type, value] = _settingSplitter->split(setting); + + if (_combinedSetting && hasOptionPrefix && !value && index + 1 < settings.size() && + !tryGetPrefix(settings[index + 1])) + { + value = settings[++index]; + } + return {keys, _valueDeserializersMap->getDeserializerFor(type).deserialize(value)}; + } + + INLINE bool SettingsParser::tryRemovePrefix(std::string_view &setting) const + { + if (const auto settingPrefix = tryGetPrefix(setting)) + { + setting.remove_prefix(settingPrefix->size()); + return true; + } + return false; + } + + INLINE std::optional SettingsParser::tryGetPrefix(std::string_view setting) const + { + for (auto &settingPrefix : _settingPrefixes) + { + if (utils::startsWith(setting, settingPrefix)) + { + return settingPrefix; + } + } + return std::nullopt; + } + +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp index aeeba45..d0ea77d 100644 --- a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp @@ -6,20 +6,53 @@ namespace sb::cf::details { - INLINE std::map & - ValueDeserializersMap::getDeserializersMap() + INLINE ValueDeserializersMap::ValueDeserializersMap(const std::string_view defaultType, + const bool throwOnUnknownType, Deserializers valueDeserializers) + : _defaultType(defaultType), _throwOnUnknownType(throwOnUnknownType) { - return _deserializersLookup; + for (auto &[type, deserializer] : valueDeserializers) + { + set(type, std::move(deserializer)); + } } - INLINE void ValueDeserializersMap::set(std::string_view type, IDeserializer::Ptr deserializer) + INLINE ValueDeserializersMap::DeserializersMap &ValueDeserializersMap::getDeserializersMap() { - _deserializersLookup[std::string{type}] = std::move(deserializer); + return _deserializersMap; } - INLINE const IDeserializer *ValueDeserializersMap::getDeserializerFor(std::string_view typeStr) const + INLINE void ValueDeserializersMap::set(const std::string_view type, IDeserializer::Ptr deserializer) { - auto it = _deserializersLookup.find(typeStr); - return it != _deserializersLookup.end() ? it->second.get() : nullptr; + _deserializersMap[std::string{type}] = std::move(deserializer); + } + + INLINE const IDeserializer &ValueDeserializersMap::getDeserializerFor( + const std::optional type) const + { + if (const auto deserializer = type ? findDeserializerFor(*type) : &getDefaultDeserializer()) + { + return *deserializer; + } + if (_throwOnUnknownType) + { + throw ConfigException("Unknown type: '" + std::string{*type} + "' deserializer for type was not found"); + } + return getDefaultDeserializer(); + } + + INLINE const IDeserializer &ValueDeserializersMap::getDefaultDeserializer() const + { + if (const auto deserializer = findDeserializerFor(_defaultType)) + { + return *deserializer; + } + throw ConfigException("Unknown default type: '" + std::string{_defaultType} + + "' deserializer for type was not found"); + } + + INLINE const IDeserializer *ValueDeserializersMap::findDeserializerFor(const std::string_view type) const + { + const auto it = _deserializersMap.find(type); + return it != _deserializersMap.end() ? it->second.get() : nullptr; } } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/SettingParser.hpp b/Include/SevenBit/Conf/Details/SettingParser.hpp index 904b401..404f43e 100644 --- a/Include/SevenBit/Conf/Details/SettingParser.hpp +++ b/Include/SevenBit/Conf/Details/SettingParser.hpp @@ -9,7 +9,6 @@ #include "SevenBit/Conf/ISettingParser.hpp" #include "SevenBit/Conf/ISettingSplitter.hpp" #include "SevenBit/Conf/IValueDeserializersMap.hpp" -#include "SevenBit/Conf/SettingParserConfig.hpp" namespace sb::cf::details { @@ -19,32 +18,16 @@ namespace sb::cf::details const ISettingSplitter::Ptr _settingSplitter; const IValueDeserializersMap::Ptr _valueDeserializersMap; - const std::string_view _defaultType; - const bool _allowEmptyKeys; - const bool _throwOnUnknownType; - public: using Ptr = std::unique_ptr; - SettingParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap, - std::string_view defaultType, bool allowEmptyKeys, bool throwOnUnknownType); + SettingParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap); [[nodiscard]] ISettingParser::Result parse(std::string_view setting) const override; [[nodiscard]] const ISettingSplitter &getSettingSplitter() const; [[nodiscard]] const IValueDeserializersMap &getValueDeserializersMap() const; - - [[nodiscard]] std::string_view getDefaultType() const; - - [[nodiscard]] bool getAllowEmptyKeys() const; - - [[nodiscard]] bool getThrowOnUnknownType() const; - - private: - [[nodiscard]] const IDeserializer &getDeserializerFor(std::string_view type) const; - - void checkKeys(const std::vector &keys) const; }; } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/SettingParserConfig.hpp b/Include/SevenBit/Conf/Details/SettingParserConfig.hpp similarity index 54% rename from Include/SevenBit/Conf/SettingParserConfig.hpp rename to Include/SevenBit/Conf/Details/SettingParserConfig.hpp index 3e8db7c..4dc8528 100644 --- a/Include/SevenBit/Conf/SettingParserConfig.hpp +++ b/Include/SevenBit/Conf/Details/SettingParserConfig.hpp @@ -9,10 +9,10 @@ namespace sb::cf { struct SettingParserConfig { - std::vector settingPrefixes = {"--"}; - std::vector settingSplitters = {"="}; - std::vector keySplitters = {":", "__"}; - std::vector typeMarkers = {"!", "___"}; + std::vector settingPrefixes; + std::vector settingSplitters; + std::vector keySplitters; + std::vector typeMarkers; std::string_view defaultType = "string"; bool throwOnUnknownType = true; bool allowEmptyKeys = false; diff --git a/Include/SevenBit/Conf/Details/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/SettingSplitter.hpp index 6ddba9f..4324d23 100644 --- a/Include/SevenBit/Conf/Details/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/SettingSplitter.hpp @@ -15,27 +15,26 @@ namespace sb::cf::details class EXPORT SettingSplitter : public ISettingSplitter { private: - const std::vector _settingPrefixes; const std::vector _settingSplitters; const std::vector _typeMarkers; const std::vector _keySplitters; + bool _allowEmptyKeys; public: - SettingSplitter(std::vector settingPrefixes, std::vector settingSplitters, - std::vector typeMarkers, std::vector keySplitters); + SettingSplitter(std::vector settingSplitters, std::vector typeMarkers, + std::vector keySplitters, bool allowEmptyKeys); [[nodiscard]] ISettingSplitter::Result split(std::string_view setting) const override; private: - [[nodiscard]] std::string_view tryRemovePrefix(std::string_view setting) const; - [[nodiscard]] std::pair> splitSetting( std::string_view setting) const; - [[nodiscard]] std::pair> splitType( - std::string_view key) const; - [[nodiscard]] std::vector splitKey(std::string_view key) const; + + [[nodiscard]] std::optional tryExtractType(std::string_view &key) const; + + void checkKeys(const std::vector &keys) const; }; } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/SettingsParser.hpp b/Include/SevenBit/Conf/Details/SettingsParser.hpp new file mode 100644 index 0000000..7b98323 --- /dev/null +++ b/Include/SevenBit/Conf/Details/SettingsParser.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/ISettingSplitter.hpp" +#include "SevenBit/Conf/ISettingsParser.hpp" +#include "SevenBit/Conf/IValueDeserializersMap.hpp" + +namespace sb::cf::details +{ + class EXPORT SettingsParser : public ISettingsParser + { + struct ArgumentParseResult + { + std::vector keys; + JsonValue value; + }; + + const ISettingSplitter::Ptr _settingSplitter; + const IValueDeserializersMap::Ptr _valueDeserializersMap; + const std::vector _settingPrefixes; + bool _combinedSetting; + + public: + using Ptr = std::unique_ptr; + + SettingsParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap, + std::vector settingPrefixes, bool combinedSetting); + + [[nodiscard]] JsonObject parse(const std::vector &settings) const override; + + private: + [[nodiscard]] ArgumentParseResult parseArgument(const std::vector &settings, + size_t &index) const; + + bool tryRemovePrefix(std::string_view &setting) const; + + [[nodiscard]] std::optional tryGetPrefix(std::string_view setting) const; + }; +} // namespace sb::cf::details + +#ifdef _7BIT_CONF_ADD_IMPL +#include "SevenBit/Conf/Details/Impl/SettingsParser.hpp" +#endif diff --git a/Include/SevenBit/Conf/Details/Utils.hpp b/Include/SevenBit/Conf/Details/Utils.hpp index f51559b..e068e7b 100644 --- a/Include/SevenBit/Conf/Details/Utils.hpp +++ b/Include/SevenBit/Conf/Details/Utils.hpp @@ -57,13 +57,26 @@ namespace sb::cf::details::utils template void assertPtr(const std::shared_ptr &ptr) { assertPtr(ptr.get()); } - template std::pair tryStringTo(std::string_view str, bool full = true) + EXPORT inline size_t startsWhiteSpace(std::string_view str) { - TNumber number = 0; - while (!str.empty() && std::isspace(str.front())) + size_t result = 0; + for (unsigned char ch : str) { - str.remove_prefix(1); + if (!std::isspace(ch)) + { + break; + } + ++result; } + return result; + } + + EXPORT inline bool isWhiteSpace(std::string_view str) { return startsWhiteSpace(str) == str.size(); } + + template std::pair tryStringTo(std::string_view str, bool full = true) + { + TNumber number = 0; + str.remove_prefix(startsWhiteSpace(str)); auto last = str.data() + str.size(); auto res = std::from_chars(str.data(), last, number); auto success = res.ec == std::errc{} && (!full || res.ptr == last); @@ -111,6 +124,24 @@ namespace sb::cf::details::utils } throw ConfigException{"Cannot convert string to number: " + std::string{str}}; } + + template + static ForwardIt removeIf(ForwardIt first, ForwardIt last, UnaryPredicate &&p) + { + first = std::find_if(first, last, p); + if (first != last) + { + for (ForwardIt i = first; ++i != last;) + { + if (!p(*i)) + { + *first++ = std::move(*i); + } + } + } + return first; + } + } // namespace sb::cf::details::utils #ifdef _7BIT_CONF_ADD_IMPL diff --git a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp index 315f3e5..d608227 100644 --- a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp @@ -14,7 +14,7 @@ namespace sb::cf::details { class EXPORT ValueDeserializersMap : public IValueDeserializersMap { - private: + public: struct CaseInsensitiveLess { using is_transparent = int; @@ -25,24 +25,36 @@ namespace sb::cf::details } }; - std::map _deserializersLookup; + using Deserializers = std::vector>; + using DeserializersMap = std::map; + + private: + DeserializersMap _deserializersMap; + const std::string_view _defaultType; + const bool _throwOnUnknownType; public: using Ptr = std::unique_ptr; - ValueDeserializersMap() = default; + explicit ValueDeserializersMap(std::string_view defaultType, bool throwOnUnknownType = true, + Deserializers valueDeserializers = {}); ValueDeserializersMap(const ValueDeserializersMap &) = delete; ValueDeserializersMap(ValueDeserializersMap &&) noexcept = default; ValueDeserializersMap &operator=(const ValueDeserializersMap &) = delete; - ValueDeserializersMap &operator=(ValueDeserializersMap &&) = default; + ValueDeserializersMap &operator=(ValueDeserializersMap &&) = delete; - [[nodiscard]] std::map &getDeserializersMap(); + [[nodiscard]] DeserializersMap &getDeserializersMap(); void set(std::string_view type, IDeserializer::Ptr deserializer); - [[nodiscard]] const IDeserializer *getDeserializerFor(std::string_view type) const override; + [[nodiscard]] const IDeserializer &getDeserializerFor(std::optional type) const override; + + private: + [[nodiscard]] const IDeserializer &getDefaultDeserializer() const; + + [[nodiscard]] const IDeserializer *findDeserializerFor(std::string_view type) const; }; } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/IConfigurationBuilder.hpp b/Include/SevenBit/Conf/IConfigurationBuilder.hpp index 1e682de..b8d830b 100644 --- a/Include/SevenBit/Conf/IConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/IConfigurationBuilder.hpp @@ -11,10 +11,11 @@ #include "SevenBit/Conf/LibraryConfig.hpp" +#include "SevenBit/Conf/CommandLineParserBuilder.hpp" +#include "SevenBit/Conf/CommandLineParserConfig.hpp" #include "SevenBit/Conf/IConfiguration.hpp" #include "SevenBit/Conf/IObject.hpp" #include "SevenBit/Conf/SettingParserBuilder.hpp" -#include "SevenBit/Conf/SettingParserConfig.hpp" #include "SevenBit/Conf/Sources/AppSettingsConfiguration.hpp" #include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" #include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" @@ -88,30 +89,30 @@ namespace sb::cf IConfigurationBuilder &addCommandLine(int argc, char *const *const argv) { - return addCommandLine(argc, argv, SettingParserConfig{}); + return addCommandLine(argc, argv, CommandLineParserConfig{}); } IConfigurationBuilder &addCommandLine(std::vector args) { - return addCommandLine(std::move(args), SettingParserConfig{}); + return addCommandLine(std::move(args), CommandLineParserConfig{}); } - IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, SettingParserConfig config) + IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, CommandLineParserConfig config) { - return addCommandLine(argc, argv, SettingParserBuilder{}.useConfig(std::move(config)).build()); + return addCommandLine(argc, argv, CommandLineParserBuilder{}.useConfig(std::move(config)).build()); } - IConfigurationBuilder &addCommandLine(std::vector args, SettingParserConfig config) + IConfigurationBuilder &addCommandLine(std::vector args, CommandLineParserConfig config) { - return addCommandLine(std::move(args), SettingParserBuilder{}.useConfig(std::move(config)).build()); + return addCommandLine(std::move(args), CommandLineParserBuilder{}.useConfig(std::move(config)).build()); } - IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, ISettingParser::Ptr parser) + IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, ISettingsParser::Ptr parser) { return add(CommandLineConfigurationSource::create(argc, argv, std::move(parser))); } - IConfigurationBuilder &addCommandLine(std::vector args, ISettingParser::Ptr parser) + IConfigurationBuilder &addCommandLine(std::vector args, ISettingsParser::Ptr parser) { return add(CommandLineConfigurationSource::create(std::move(args), std::move(parser))); } diff --git a/Include/SevenBit/Conf/IDeserializer.hpp b/Include/SevenBit/Conf/IDeserializer.hpp index daee04d..03ebdb4 100644 --- a/Include/SevenBit/Conf/IDeserializer.hpp +++ b/Include/SevenBit/Conf/IDeserializer.hpp @@ -2,7 +2,6 @@ #include #include -#include #include "SevenBit/Conf/LibraryConfig.hpp" diff --git a/Include/SevenBit/Conf/ISettingsParser.hpp b/Include/SevenBit/Conf/ISettingsParser.hpp new file mode 100644 index 0000000..5a3ca8d --- /dev/null +++ b/Include/SevenBit/Conf/ISettingsParser.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/Json.hpp" + +namespace sb::cf +{ + struct ISettingsParser + { + using Ptr = std::unique_ptr; + + [[nodiscard]] virtual JsonObject parse(const std::vector &settings) const = 0; + + virtual ~ISettingsParser() = default; + }; +} // namespace sb::cf diff --git a/Include/SevenBit/Conf/IValueDeserializersMap.hpp b/Include/SevenBit/Conf/IValueDeserializersMap.hpp index 44ed9de..d719139 100644 --- a/Include/SevenBit/Conf/IValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/IValueDeserializersMap.hpp @@ -13,7 +13,7 @@ namespace sb::cf { using Ptr = std::unique_ptr; - [[nodiscard]] virtual const IDeserializer *getDeserializerFor(std::string_view type) const = 0; + [[nodiscard]] virtual const IDeserializer &getDeserializerFor(std::optional type) const = 0; virtual ~IValueDeserializersMap() = default; }; diff --git a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp new file mode 100644 index 0000000..605a5b2 --- /dev/null +++ b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include "SevenBit/Conf/CommandLineParserBuilder.hpp" +#include "SevenBit/Conf/Details/DefaultDeserializers.hpp" +#include "SevenBit/Conf/Details/Deserializers.hpp" +#include "SevenBit/Conf/Details/SettingParser.hpp" +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/SettingsParser.hpp" +#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" + +namespace sb::cf +{ + INLINE CommandLineParserBuilder &CommandLineParserBuilder::useSplitter(ISettingSplitter::Ptr splitter) + { + _splitter = std::move(splitter); + return *this; + } + + INLINE CommandLineParserBuilder &CommandLineParserBuilder::useValueDeserializersMap( + IValueDeserializersMap::Ptr valueDeserializersMap) + { + _valueDeserializersMap = std::move(valueDeserializersMap); + return *this; + } + + INLINE CommandLineParserBuilder &CommandLineParserBuilder::useValueDeserializer( + std::string_view type, IDeserializer::Ptr valueDeserializer) + { + _valueDeserializers.emplace_back(type, std::move(valueDeserializer)); + return *this; + } + + INLINE CommandLineParserBuilder &CommandLineParserBuilder::useConfig(CommandLineParserConfig config) + { + _config = std::move(config); + return *this; + } + + INLINE CommandLineParserBuilder &CommandLineParserBuilder::useDefaultValueDeserializers() + { + DefaultDeserializers::addDefault(_valueDeserializers); + return *this; + } + + INLINE ISettingsParser::Ptr CommandLineParserBuilder::build() + { + auto hadWhiteSpaceSplitters = tryRemoveWhiteSpaceSplitters(); + return std::make_unique(getSplitter(), getValueDeserializersMap(), + std::move(getConfig().optionPrefixes), hadWhiteSpaceSplitters); + } + + INLINE bool CommandLineParserBuilder::tryRemoveWhiteSpaceSplitters() + { + auto &splitters = getConfig().optionSplitters; + const auto it = details::utils::removeIf(splitters.begin(), splitters.end(), + [](auto splitter) { return details::utils::isWhiteSpace(splitter); }); + const auto removeCnt = splitters.end() - it; + splitters.erase(it, splitters.end()); + return removeCnt; + } + + INLINE ISettingSplitter::Ptr CommandLineParserBuilder::getSplitter() + { + if (!_splitter) + { + auto &config = getConfig(); + useSplitter(std::make_unique( + std::move(config.optionSplitters), std::move(config.optionTypeMarkers), + std::move(config.optionKeySplitters), config.allowEmptyKeys)); + } + return std::move(_splitter); + } + + INLINE IValueDeserializersMap::Ptr CommandLineParserBuilder::getValueDeserializersMap() + { + if (!_valueDeserializersMap) + { + if (_valueDeserializers.empty()) + { + useDefaultValueDeserializers(); + } + auto &config = getConfig(); + useValueDeserializersMap(std::make_unique( + config.defaultType, config.throwOnUnknownType, std::move(_valueDeserializers))); + } + return std::move(_valueDeserializersMap); + } + + INLINE CommandLineParserConfig &CommandLineParserBuilder::getConfig() + { + if (!_config) + { + useConfig({}); + } + return *_config; + } +} // namespace sb::cf diff --git a/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp b/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp index 7bbc664..a4d020e 100644 --- a/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp @@ -3,6 +3,7 @@ #include "SevenBit/Conf/Details/Deserializers.hpp" #include "SevenBit/Conf/Details/SettingParser.hpp" #include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/SettingsParser.hpp" #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" #include "SevenBit/Conf/SettingParserBuilder.hpp" @@ -25,7 +26,7 @@ namespace sb::cf INLINE SettingParserBuilder &SettingParserBuilder::useValueDeserializer(std::string_view type, IDeserializer::Ptr valueDeserializer) { - _deserializersMap.emplace_back(type, std::move(valueDeserializer)); + _valueDeserializers.emplace_back(type, std::move(valueDeserializer)); return *this; } @@ -49,9 +50,7 @@ namespace sb::cf INLINE ISettingParser::Ptr SettingParserBuilder::build() { - auto &config = getConfig(); - return std::make_unique(getSplitter(), getValueDeserializersMap(), config.defaultType, - config.allowEmptyKeys, config.throwOnUnknownType); + return std::make_unique(getSplitter(), getValueDeserializersMap()); } INLINE ISettingSplitter::Ptr SettingParserBuilder::getSplitter() @@ -60,8 +59,8 @@ namespace sb::cf { auto &config = getConfig(); useSplitter(std::make_unique( - std::move(config.settingPrefixes), std::move(config.settingSplitters), std::move(config.typeMarkers), - std::move(config.keySplitters))); + std::move(config.settingSplitters), std::move(config.typeMarkers), std::move(config.keySplitters), + config.allowEmptyKeys)); } return std::move(_splitter); } @@ -70,16 +69,13 @@ namespace sb::cf { if (!_valueDeserializersMap) { - if (_deserializersMap.empty()) + if (_valueDeserializers.empty()) { useDefaultValueDeserializers(); } - auto valueDeserializers = std::make_unique(); - for (auto &[type, deserializer] : _deserializersMap) - { - valueDeserializers->set(type, std::move(deserializer)); - } - useValueDeserializersMap(std::move(valueDeserializers)); + auto &config = getConfig(); + useValueDeserializersMap(std::make_unique( + config.defaultType, config.throwOnUnknownType, std::move(_valueDeserializers))); } return std::move(_valueDeserializersMap); } diff --git a/Include/SevenBit/Conf/SettingParserBuilder.hpp b/Include/SevenBit/Conf/SettingParserBuilder.hpp index 6db05a3..39e8135 100644 --- a/Include/SevenBit/Conf/SettingParserBuilder.hpp +++ b/Include/SevenBit/Conf/SettingParserBuilder.hpp @@ -7,10 +7,10 @@ #include "SevenBit/Conf/LibraryConfig.hpp" +#include "SevenBit/Conf/Details/SettingParserConfig.hpp" #include "SevenBit/Conf/ISettingParser.hpp" #include "SevenBit/Conf/ISettingSplitter.hpp" #include "SevenBit/Conf/IValueDeserializersMap.hpp" -#include "SevenBit/Conf/SettingParserConfig.hpp" namespace sb::cf { @@ -20,7 +20,7 @@ namespace sb::cf private: ISettingSplitter::Ptr _splitter; IValueDeserializersMap::Ptr _valueDeserializersMap; - std::vector> _deserializersMap; + std::vector> _valueDeserializers; std::optional _config; public: diff --git a/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp index e772403..05fb290 100644 --- a/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp @@ -15,23 +15,21 @@ namespace sb::cf { private: std::vector _args; - ISettingParser::Ptr _parser; + ISettingsParser::Ptr _parser; - CommandLineConfigurationSource(std::vector args, ISettingParser::Ptr parser); + CommandLineConfigurationSource(std::vector args, ISettingsParser::Ptr parser); public: using Ptr = std::unique_ptr; using SPtr = std::shared_ptr; - [[nodiscard]] static SPtr create(int argc, const char *const *argv, - ISettingParser::Ptr parser = SettingParserBuilder{}.build()); + [[nodiscard]] static SPtr create(int argc, const char *const *argv, ISettingsParser::Ptr parser); - [[nodiscard]] static SPtr create(std::vector args, - ISettingParser::Ptr parser = SettingParserBuilder{}.build()); + [[nodiscard]] static SPtr create(std::vector args, ISettingsParser::Ptr parser); [[nodiscard]] const std::vector &getArgs() const; - [[nodiscard]] const ISettingParser &getSettingParser() const; + [[nodiscard]] const ISettingsParser &getSettingsParser() const; IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; }; diff --git a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp index 4018331..c3fcfaf 100644 --- a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp @@ -3,11 +3,10 @@ #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" - namespace sb::cf { INLINE CommandLineConfigurationSource::CommandLineConfigurationSource(std::vector args, - ISettingParser::Ptr parser) + ISettingsParser::Ptr parser) : _args(std::move(args)), _parser(std::move(parser)) { details::utils::assertPtr(_parser); @@ -15,7 +14,7 @@ namespace sb::cf INLINE CommandLineConfigurationSource::SPtr CommandLineConfigurationSource::create(int argc, const char *const *argv, - ISettingParser::Ptr parser) + ISettingsParser::Ptr parser) { std::vector args; if (argc > 1) @@ -30,7 +29,7 @@ namespace sb::cf } INLINE CommandLineConfigurationSource::SPtr CommandLineConfigurationSource::create( - std::vector args, ISettingParser::Ptr parser) + std::vector args, ISettingsParser::Ptr parser) { return CommandLineConfigurationSource::SPtr( new CommandLineConfigurationSource{std::move(args), std::move(parser)}); @@ -38,7 +37,7 @@ namespace sb::cf INLINE const std::vector &CommandLineConfigurationSource::getArgs() const { return _args; } - INLINE const ISettingParser &CommandLineConfigurationSource::getSettingParser() const { return *_parser; } + INLINE const ISettingsParser &CommandLineConfigurationSource::getSettingsParser() const { return *_parser; } INLINE IConfigurationProvider::Ptr CommandLineConfigurationSource::build(IConfigurationBuilder &builder) { @@ -55,11 +54,6 @@ namespace sb::cf INLINE void CommandLineConfigurationProvider::load() { clear(); - auto &parser = _source->getSettingParser(); - for (auto &setting : _source->getArgs()) - { - auto [keys, value] = parser.parse(setting); - update(keys, std::move(value)); - } + set(_source->getSettingsParser().parse(_source->getArgs())); } } // namespace sb::cf diff --git a/Source/Source.cpp b/Source/Source.cpp index 7be1443..af52a0b 100644 --- a/Source/Source.cpp +++ b/Source/Source.cpp @@ -1,24 +1,26 @@ #include "ConfigCheck.hpp" +#include "SevenBit/Conf/Details/Impl/CommandLineParser.hpp" #include "SevenBit/Conf/Details/Impl/Configuration.hpp" #include "SevenBit/Conf/Details/Impl/Deserializers.hpp" #include "SevenBit/Conf/Details/Impl/JsonObjectExt.hpp" #include "SevenBit/Conf/Details/Impl/SettingParser.hpp" +#include "SevenBit/Conf/Details/Impl/SettingsParser.hpp" #include "SevenBit/Conf/Details/Impl/SettingSplitter.hpp" #include "SevenBit/Conf/Details/Impl/Utils.hpp" #include "SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp" #include "SevenBit/Conf/Impl/ConfigurationBuilder.hpp" #include "SevenBit/Conf/Impl/Exceptions.hpp" #include "SevenBit/Conf/Impl/SettingParserBuilder.hpp" -#include "SevenBit/Conf/Sources//Impl/AppSettingsConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/ChainedConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/CommandLineConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/ConfigurationProviderBase.hpp" -#include "SevenBit/Conf/Sources//Impl/EnvironmentVarsConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/InMemoryConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/JsonConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/JsonFileConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/JsonStreamConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/KeyPerFileConfiguration.hpp" -#include "SevenBit/Conf/Sources//Impl/MapConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/AppSettingsConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp" +#include "SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp" +#include "SevenBit/Conf/Sources/Impl/MapConfiguration.hpp" diff --git a/Tests/Unit/CommandLineConfigurationTest.cpp b/Tests/Unit/CommandLineConfigurationTest.cpp index fe14cea..1018939 100644 --- a/Tests/Unit/CommandLineConfigurationTest.cpp +++ b/Tests/Unit/CommandLineConfigurationTest.cpp @@ -1,93 +1,93 @@ -#include -#include - -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" - -class CommandLineConfigurationTest : public testing::Test -{ - protected: - ConfigurationBuilderMock mock; - - static void TearUpTestSuite() {} - - CommandLineConfigurationTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) -{ - std::vector args; - EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), - sb::cf::NullPointerException); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) -{ - const char *const argv[] = { - "program/path", "--string___string=test", "--list:0!string=string", "double!double=1.22", - "true_bool!bool=-1", "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", - "--list:1=string1", "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", - "--int_list:1___int=22", "int_list__2!int=11", - }; - int size = sizeof(argv) / sizeof(char *); - auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"string", "test"}, - {"double", 1.22}, - {"false_bool", false}, - {"false_bool2", false}, - {"true_bool", true}, - {"true_bool2", true}, - {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"int_list", sb::cf::JsonArray{33, 22, 11}}, - {"object", {{"inner", {{"object", "string"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) -{ - std::vector args = { - "--string___string=test", "--list:0!string=string", "double!double=1.22", "true_bool!bool=-1", - "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", "--list:1=string1", - "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", "--int_list:1___int=22", - "int_list__2!int=11", - }; - auto provider = sb::cf::CommandLineConfigurationSource::create(args)->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"string", "test"}, - {"double", 1.22}, - {"false_bool", false}, - {"false_bool2", false}, - {"true_bool", true}, - {"true_bool2", true}, - {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"int_list", sb::cf::JsonArray{33, 22, 11}}, - {"object", {{"inner", {{"object", "string"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) -{ - const char *argv[] = { - "exec/path", - }; - int size = sizeof(argv) / sizeof(char *); - auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); - - provider->load(); - - EXPECT_TRUE(provider->getConfiguration().empty()); -} +// #include +// #include +// +// #include "Mocks/ConfigurationBuilderMock.hpp" +// #include "SevenBit/Conf/Exceptions.hpp" +// #include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" +// +// class CommandLineConfigurationTest : public testing::Test +// { +// protected: +// ConfigurationBuilderMock mock; +// +// static void TearUpTestSuite() {} +// +// CommandLineConfigurationTest() {} +// +// void SetUp() override {} +// +// void TearDown() override {} +// +// static void TearDownTestSuite() {} +// }; +// +// TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) +// { +// std::vector args; +// EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), +// sb::cf::NullPointerException); +// } +// +// TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) +// { +// const char *const argv[] = { +// "program/path", "--string___string=test", "--list:0!string=string", "double!double=1.22", +// "true_bool!bool=-1", "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", +// "--list:1=string1", "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", +// "--int_list:1___int=22", "int_list__2!int=11", +// }; +// int size = sizeof(argv) / sizeof(char *); +// auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); +// +// provider->load(); +// +// sb::cf::JsonObject expected = {{"string", "test"}, +// {"double", 1.22}, +// {"false_bool", false}, +// {"false_bool2", false}, +// {"true_bool", true}, +// {"true_bool2", true}, +// {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, +// {"int_list", sb::cf::JsonArray{33, 22, 11}}, +// {"object", {{"inner", {{"object", "string"}}}}}}; +// +// EXPECT_EQ(provider->getConfiguration(), expected); +// } +// +// TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) +// { +// std::vector args = { +// "--string___string=test", "--list:0!string=string", "double!double=1.22", "true_bool!bool=-1", +// "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", "--list:1=string1", +// "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", "--int_list:1___int=22", +// "int_list__2!int=11", +// }; +// auto provider = sb::cf::CommandLineConfigurationSource::create(args)->build(mock); +// +// provider->load(); +// +// sb::cf::JsonObject expected = {{"string", "test"}, +// {"double", 1.22}, +// {"false_bool", false}, +// {"false_bool2", false}, +// {"true_bool", true}, +// {"true_bool2", true}, +// {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, +// {"int_list", sb::cf::JsonArray{33, 22, 11}}, +// {"object", {{"inner", {{"object", "string"}}}}}}; +// +// EXPECT_EQ(provider->getConfiguration(), expected); +// } +// +// TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) +// { +// const char *argv[] = { +// "exec/path", +// }; +// int size = sizeof(argv) / sizeof(char *); +// auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); +// +// provider->load(); +// +// EXPECT_TRUE(provider->getConfiguration().empty()); +// } diff --git a/Tests/Unit/DeserializersTest.cpp b/Tests/Unit/DeserializersTest.cpp index 28dc776..9fb5536 100644 --- a/Tests/Unit/DeserializersTest.cpp +++ b/Tests/Unit/DeserializersTest.cpp @@ -1,237 +1,237 @@ -#include -#include -#include -#include -#include -#include - -#include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -#include "Utilities/ParamsTest.hpp" - -class DeserializersTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - DeserializersTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap() -{ - sb::cf::details::ValueDeserializersMap deserializers; - deserializers.set("string", std::make_unique()); - deserializers.set("bool", std::make_unique()); - deserializers.set("int", std::make_unique()); - deserializers.set("double", std::make_unique()); - deserializers.set("uint", std::make_unique()); - deserializers.set("json", std::make_unique()); - deserializers.set("null", std::make_unique()); - return std::move(deserializers); -} - -static Params, sb::cf::JsonValue> DeserializeData = { - // Int - {"int", "12", std::int64_t{12}}, - {"int", "-12", std::int64_t{-12}}, - {"Int", "-12", std::int64_t{-12}}, - {"INT", "-12", std::int64_t{-12}}, - {"InT", "-12", std::int64_t{-12}}, - {"int", std::nullopt, std::int64_t{0}}, - {"int", "1", std::int64_t{1}}, - {"int", " 1", std::int64_t{1}}, - {"int", "\t\n 1", std::int64_t{1}}, - {"int", "1", std::int64_t{1}}, - {"Int", "1", std::int64_t{1}}, - {"InT", "1", std::int64_t{1}}, - {"INT", "1", std::int64_t{1}}, - {"int", "-1234", std::int64_t{-1234}}, - {"int", "1234", std::int64_t{1234}}, - {"int", "-223372036854775807", std::int64_t{-223372036854775807ll}}, - {"int", "223372036854775807", std::int64_t{223372036854775807ll}}, - - // UInt - {"uint", "12", std::uint64_t{12}}, - {"Uint", "12", std::uint64_t{12}}, - {"UINT", "12", std::uint64_t{12}}, - {"UiNt", "12", std::uint64_t{12}}, - {"uint", "1222", std::uint64_t{1222}}, - {"uint", std::nullopt, std::uint64_t{0}}, - {"uint", "1", std::uint64_t{1}}, - {"uint", " 1", std::uint64_t{1}}, - {"uint", "\t\n 1", std::uint64_t{1}}, - {"uInt", "1", std::uint64_t{1}}, - {"uInT", "1", std::uint64_t{1}}, - {"UINT", "1", std::uint64_t{1}}, - {"uint", "1234", std::uint64_t{1234}}, - {"uint", "223372036854775807", std::uint64_t{223372036854775807ll}}, - {"uint", "17446744073709551615", std::uint64_t{17446744073709551615ull}}, - - // Bool - {"bool", "true", true}, - {"BoOl", "true", true}, - {"bool", "1", true}, - {"bool", "-11", true}, - {"bool", "0", false}, - {"Bool", "true", true}, - {"BOOL", "true", true}, - {"bool", "True", true}, - {"bool", "TrUe", true}, - {"bool", " 1", true}, - {"bool", "\t\n 1", true}, - {"bool", "-1", true}, - {"bool", "-1123", true}, - {"bool", "1123", true}, - {"bool", "false", false}, - {"bool", "False", false}, - {"bool", "FAlSe", false}, - {"bool", std::nullopt, false}, - // String - {"string", "hello", std::string{"hello"}}, - {"String", "hello", std::string{"hello"}}, - {"STRING", "hello", std::string{"hello"}}, - {"STRING", "hello", std::string{"hello"}}, - {"String", "hello", std::string{"hello"}}, - {"StRinG", "hell\to", std::string{"hell\to"}}, - {"string", "hello", std::string{"hello"}}, - {"string", "", std::string{""}}, - {"string", std::nullopt, std::string{""}}, - // Double - {"double", "1.1", 1.1}, - {"Double", "1.1", 1.1}, - {"DOUBLE", "1.1", 1.1}, - {"DoUBle", "1.1", 1.1}, - {"double", "-11.1", -11.1}, - {"double", "1", 1.0}, - {"double", " 1", 1.0}, - {"double", "\t\n 1", 1.0}, - {"Double", "1", 1.0}, - {"dOuBle", "1", 1.0}, - {"DOUBLE", "1", 1.0}, - {"double", "1.123", 1.123}, - {"double", "-12.21", -12.21}, - {"double", "10000.11", 10000.11}, - {"double", "-10000.11", -10000.11}, - {"double", std::nullopt, 0.0}, - // Null - {"null", "hello", sb::cf::json::null}, - {"Null", "hello", sb::cf::json::null}, - {"NULL", "hello", sb::cf::json::null}, - {"nULl", "hello", sb::cf::json::null}, - {"null", "1", sb::cf::json::null}, - {"null", "asdqwdwq", sb::cf::json::null}, - {"Null", "1", sb::cf::json::null}, - {"NULL", "1asdqwdwq", sb::cf::json::null}, - {"NuLl", "1.123 asdqwdwq", sb::cf::json::null}, - {"null", std::nullopt, sb::cf::json::null}, - // Json - {"json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"Json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"JSON", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"jSOn", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"JSon", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, - {"json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"JsOn", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, - {"json", R"({"hello": [1]})", sb::cf::JsonObject{{"hello", sb::cf::JsonArray{1}}}}, - {"json", R"({"hello": null})", sb::cf::JsonObject{{"hello", sb::cf::json::null}}}, - -}; -PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) -{ - const auto &[type, value, expected] = GetParam(); - auto deserializers = makeDefaultDeserializersMap(); - - auto deserializer = deserializers.getDeserializerFor(type); - EXPECT_TRUE(deserializer); - EXPECT_EQ(deserializer->deserialize(value), expected); -} - -TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) -{ - auto deserializers = makeDefaultDeserializersMap(); - - auto deserializer = deserializers.getDeserializerFor("json"); - EXPECT_EQ((deserializer->deserialize(std::nullopt)), (sb::cf::JsonValue{})); -} - -TEST_F(DeserializersTest, ShouldNotFoundDeserializer) -{ - auto deserializers = makeDefaultDeserializersMap(); - - deserializers.set("unknown2", nullptr); - - EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); - EXPECT_FALSE(deserializers.getDeserializerFor("unknown")); - EXPECT_FALSE(deserializers.getDeserializerFor("unknown2")); -} - -static Params> FailDeserializeValues = { - // Int - {"int", "123 abcd"}, - {"int", "123 "}, - {"int", "123.123"}, - {"int", " as 123 abcd"}, - {"int", "abcd"}, - {"int", " "}, - {"int", ""}, - {"int", "\n"}, - // UInt - {"uint", "123 abcd"}, - {"uint", "123 "}, - {"uint", "-12"}, - {"uint", "123.123"}, - {"uint", " as 123 abcd"}, - {"uint", "abcd"}, - {"uint", " "}, - {"uint", ""}, - {"uint", "\n"}, - // Bool - {"bool", "123 abcd"}, - {"bool", "123.123"}, - {"bool", " as 123 abcd"}, - {"bool", "abcd"}, - {"bool", " "}, - {"bool", ""}, - {"bool", "\n"}, - {"bool", "ttruee"}, - {"bool", "ffalsee"}, - {"bool", "false "}, - {"bool", "false "}, - {"bool", " false"}, - // Double - {"double", "123 abcd"}, - {"double", "123 "}, - {"double", "123 "}, - {"double", " as 123 abcd"}, - {"double", "abcd"}, - {"double", " "}, - {"double", ""}, - {"double", "\n"}, - // Json - {"json", "123 abcd"}, - {"json", " as 123 abcd"}, - {"json", "abcd"}, - {"json", "{\"hello: 123}"}, - {"json", "{\"hello\": sde123}"}, - {"json", " "}, - {"json", ""}, - {"json", "\n"}, -}; -PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) -{ - const auto &[type, value] = GetParam(); - auto deserializers = makeDefaultDeserializersMap(); - - auto deserializer = deserializers.getDeserializerFor(type); - - EXPECT_TRUE(deserializer); - EXPECT_ANY_THROW(auto result = deserializer->deserialize(value)); -} +// #include +// #include +// #include +// #include +// #include +// #include +// +// #include "SevenBit/Conf/Details/Deserializers.hpp" +// #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +// #include "Utilities/ParamsTest.hpp" +// +// class DeserializersTest : public testing::Test +// { +// protected: +// static void TearUpTestSuite() {} +// +// DeserializersTest() {} +// +// void SetUp() override {} +// +// void TearDown() override {} +// +// static void TearDownTestSuite() {} +// }; +// +// sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap() +// { +// sb::cf::details::ValueDeserializersMap deserializers; +// deserializers.set("string", std::make_unique()); +// deserializers.set("bool", std::make_unique()); +// deserializers.set("int", std::make_unique()); +// deserializers.set("double", std::make_unique()); +// deserializers.set("uint", std::make_unique()); +// deserializers.set("json", std::make_unique()); +// deserializers.set("null", std::make_unique()); +// return std::move(deserializers); +// } +// +// static Params, sb::cf::JsonValue> DeserializeData = { +// // Int +// {"int", "12", std::int64_t{12}}, +// {"int", "-12", std::int64_t{-12}}, +// {"Int", "-12", std::int64_t{-12}}, +// {"INT", "-12", std::int64_t{-12}}, +// {"InT", "-12", std::int64_t{-12}}, +// {"int", std::nullopt, std::int64_t{0}}, +// {"int", "1", std::int64_t{1}}, +// {"int", " 1", std::int64_t{1}}, +// {"int", "\t\n 1", std::int64_t{1}}, +// {"int", "1", std::int64_t{1}}, +// {"Int", "1", std::int64_t{1}}, +// {"InT", "1", std::int64_t{1}}, +// {"INT", "1", std::int64_t{1}}, +// {"int", "-1234", std::int64_t{-1234}}, +// {"int", "1234", std::int64_t{1234}}, +// {"int", "-223372036854775807", std::int64_t{-223372036854775807ll}}, +// {"int", "223372036854775807", std::int64_t{223372036854775807ll}}, +// +// // UInt +// {"uint", "12", std::uint64_t{12}}, +// {"Uint", "12", std::uint64_t{12}}, +// {"UINT", "12", std::uint64_t{12}}, +// {"UiNt", "12", std::uint64_t{12}}, +// {"uint", "1222", std::uint64_t{1222}}, +// {"uint", std::nullopt, std::uint64_t{0}}, +// {"uint", "1", std::uint64_t{1}}, +// {"uint", " 1", std::uint64_t{1}}, +// {"uint", "\t\n 1", std::uint64_t{1}}, +// {"uInt", "1", std::uint64_t{1}}, +// {"uInT", "1", std::uint64_t{1}}, +// {"UINT", "1", std::uint64_t{1}}, +// {"uint", "1234", std::uint64_t{1234}}, +// {"uint", "223372036854775807", std::uint64_t{223372036854775807ll}}, +// {"uint", "17446744073709551615", std::uint64_t{17446744073709551615ull}}, +// +// // Bool +// {"bool", "true", true}, +// {"BoOl", "true", true}, +// {"bool", "1", true}, +// {"bool", "-11", true}, +// {"bool", "0", false}, +// {"Bool", "true", true}, +// {"BOOL", "true", true}, +// {"bool", "True", true}, +// {"bool", "TrUe", true}, +// {"bool", " 1", true}, +// {"bool", "\t\n 1", true}, +// {"bool", "-1", true}, +// {"bool", "-1123", true}, +// {"bool", "1123", true}, +// {"bool", "false", false}, +// {"bool", "False", false}, +// {"bool", "FAlSe", false}, +// {"bool", std::nullopt, false}, +// // String +// {"string", "hello", std::string{"hello"}}, +// {"String", "hello", std::string{"hello"}}, +// {"STRING", "hello", std::string{"hello"}}, +// {"STRING", "hello", std::string{"hello"}}, +// {"String", "hello", std::string{"hello"}}, +// {"StRinG", "hell\to", std::string{"hell\to"}}, +// {"string", "hello", std::string{"hello"}}, +// {"string", "", std::string{""}}, +// {"string", std::nullopt, std::string{""}}, +// // Double +// {"double", "1.1", 1.1}, +// {"Double", "1.1", 1.1}, +// {"DOUBLE", "1.1", 1.1}, +// {"DoUBle", "1.1", 1.1}, +// {"double", "-11.1", -11.1}, +// {"double", "1", 1.0}, +// {"double", " 1", 1.0}, +// {"double", "\t\n 1", 1.0}, +// {"Double", "1", 1.0}, +// {"dOuBle", "1", 1.0}, +// {"DOUBLE", "1", 1.0}, +// {"double", "1.123", 1.123}, +// {"double", "-12.21", -12.21}, +// {"double", "10000.11", 10000.11}, +// {"double", "-10000.11", -10000.11}, +// {"double", std::nullopt, 0.0}, +// // Null +// {"null", "hello", sb::cf::json::null}, +// {"Null", "hello", sb::cf::json::null}, +// {"NULL", "hello", sb::cf::json::null}, +// {"nULl", "hello", sb::cf::json::null}, +// {"null", "1", sb::cf::json::null}, +// {"null", "asdqwdwq", sb::cf::json::null}, +// {"Null", "1", sb::cf::json::null}, +// {"NULL", "1asdqwdwq", sb::cf::json::null}, +// {"NuLl", "1.123 asdqwdwq", sb::cf::json::null}, +// {"null", std::nullopt, sb::cf::json::null}, +// // Json +// {"json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, +// {"Json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, +// {"JSON", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, +// {"jSOn", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, +// {"JSon", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, +// {"json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, +// {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, +// {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, +// {"JsOn", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, +// {"json", R"({"hello": [1]})", sb::cf::JsonObject{{"hello", sb::cf::JsonArray{1}}}}, +// {"json", R"({"hello": null})", sb::cf::JsonObject{{"hello", sb::cf::json::null}}}, +// +// }; +// PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) +// { +// const auto &[type, value, expected] = GetParam(); +// auto deserializers = makeDefaultDeserializersMap(); +// +// auto deserializer = deserializers.getDeserializerFor(type); +// EXPECT_TRUE(deserializer); +// EXPECT_EQ(deserializer->deserialize(value), expected); +// } +// +// TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) +// { +// auto deserializers = makeDefaultDeserializersMap(); +// +// auto deserializer = deserializers.getDeserializerFor("json"); +// EXPECT_EQ((deserializer->deserialize(std::nullopt)), (sb::cf::JsonValue{})); +// } +// +// TEST_F(DeserializersTest, ShouldNotFoundDeserializer) +// { +// auto deserializers = makeDefaultDeserializersMap(); +// +// deserializers.set("unknown2", nullptr); +// +// EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); +// EXPECT_FALSE(deserializers.getDeserializerFor("unknown")); +// EXPECT_FALSE(deserializers.getDeserializerFor("unknown2")); +// } +// +// static Params> FailDeserializeValues = { +// // Int +// {"int", "123 abcd"}, +// {"int", "123 "}, +// {"int", "123.123"}, +// {"int", " as 123 abcd"}, +// {"int", "abcd"}, +// {"int", " "}, +// {"int", ""}, +// {"int", "\n"}, +// // UInt +// {"uint", "123 abcd"}, +// {"uint", "123 "}, +// {"uint", "-12"}, +// {"uint", "123.123"}, +// {"uint", " as 123 abcd"}, +// {"uint", "abcd"}, +// {"uint", " "}, +// {"uint", ""}, +// {"uint", "\n"}, +// // Bool +// {"bool", "123 abcd"}, +// {"bool", "123.123"}, +// {"bool", " as 123 abcd"}, +// {"bool", "abcd"}, +// {"bool", " "}, +// {"bool", ""}, +// {"bool", "\n"}, +// {"bool", "ttruee"}, +// {"bool", "ffalsee"}, +// {"bool", "false "}, +// {"bool", "false "}, +// {"bool", " false"}, +// // Double +// {"double", "123 abcd"}, +// {"double", "123 "}, +// {"double", "123 "}, +// {"double", " as 123 abcd"}, +// {"double", "abcd"}, +// {"double", " "}, +// {"double", ""}, +// {"double", "\n"}, +// // Json +// {"json", "123 abcd"}, +// {"json", " as 123 abcd"}, +// {"json", "abcd"}, +// {"json", "{\"hello: 123}"}, +// {"json", "{\"hello\": sde123}"}, +// {"json", " "}, +// {"json", ""}, +// {"json", "\n"}, +// }; +// PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) +// { +// const auto &[type, value] = GetParam(); +// auto deserializers = makeDefaultDeserializersMap(); +// +// auto deserializer = deserializers.getDeserializerFor(type); +// +// EXPECT_TRUE(deserializer); +// EXPECT_ANY_THROW(auto result = deserializer->deserialize(value)); +// } diff --git a/Tests/Unit/SettingParserBuilderTest.cpp b/Tests/Unit/SettingParserBuilderTest.cpp index 52a39dc..45e3f09 100644 --- a/Tests/Unit/SettingParserBuilderTest.cpp +++ b/Tests/Unit/SettingParserBuilderTest.cpp @@ -1,108 +1,108 @@ -#include -#include - -#include "Mocks/DeserializerMock.hpp" -#include "Mocks/SettingSplitterMock.hpp" -#include "Mocks/ValueDeserializersMapMock.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -#include "SevenBit/Conf/SettingParserBuilder.hpp" - -class SettingParserBuilderTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - SettingParserBuilderTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(SettingParserBuilderTest, ShouldBuildDefault) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); -} - -TEST_F(SettingParserBuilderTest, ShouldUseCustomConfig) -{ - sb::cf::SettingParserBuilder builder; - - sb::cf::SettingParserConfig config; - config.defaultType = "int"; - config.throwOnUnknownType = false; - config.allowEmptyKeys = true; - auto parser = builder.useConfig(config).build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "int"); - EXPECT_TRUE(casted.getAllowEmptyKeys()); - EXPECT_FALSE(casted.getThrowOnUnknownType()); -} - -TEST_F(SettingParserBuilderTest, ShouldUseValueDeserializer) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.useDefaultValueDeserializers() - .useValueDeserializer("newType", std::make_unique()) - .build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); - EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("newType")); - EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("int")); - EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("bool")); -} - -TEST_F(SettingParserBuilderTest, ShouldUseCustomValueDeserializerMap) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); -} - -TEST_F(SettingParserBuilderTest, ShouldUseCustomSplitter) -{ - sb::cf::SettingParserBuilder builder; - - auto parser = builder.useSplitter(std::make_unique()).build(); - - auto &casted = dynamic_cast(*parser); - - EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); - EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); - EXPECT_EQ(casted.getDefaultType(), "string"); - EXPECT_FALSE(casted.getAllowEmptyKeys()); - EXPECT_TRUE(casted.getThrowOnUnknownType()); -} +// #include +// #include +// +// #include "Mocks/DeserializerMock.hpp" +// #include "Mocks/SettingSplitterMock.hpp" +// #include "Mocks/ValueDeserializersMapMock.hpp" +// #include "SevenBit/Conf/Details/SettingParser.hpp" +// #include "SevenBit/Conf/Details/SettingSplitter.hpp" +// #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +// #include "SevenBit/Conf/SettingParserBuilder.hpp" +// +// class SettingParserBuilderTest : public testing::Test +// { +// protected: +// static void TearUpTestSuite() {} +// +// SettingParserBuilderTest() {} +// +// void SetUp() override {} +// +// void TearDown() override {} +// +// static void TearDownTestSuite() {} +// }; +// +// TEST_F(SettingParserBuilderTest, ShouldBuildDefault) +// { +// sb::cf::SettingParserBuilder builder; +// +// auto parser = builder.build(); +// +// auto &casted = dynamic_cast(*parser); +// +// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); +// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +// EXPECT_EQ(casted.getDefaultType(), "string"); +// EXPECT_FALSE(casted.getAllowEmptyKeys()); +// EXPECT_TRUE(casted.getThrowOnUnknownType()); +// } +// +// TEST_F(SettingParserBuilderTest, ShouldUseCustomConfig) +// { +// sb::cf::SettingParserBuilder builder; +// +// sb::cf::SettingParserConfig config; +// config.defaultType = "int"; +// config.throwOnUnknownType = false; +// config.allowEmptyKeys = true; +// auto parser = builder.useConfig(config).build(); +// +// auto &casted = dynamic_cast(*parser); +// +// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); +// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +// EXPECT_EQ(casted.getDefaultType(), "int"); +// EXPECT_TRUE(casted.getAllowEmptyKeys()); +// EXPECT_FALSE(casted.getThrowOnUnknownType()); +// } +// +// TEST_F(SettingParserBuilderTest, ShouldUseValueDeserializer) +// { +// sb::cf::SettingParserBuilder builder; +// +// auto parser = builder.useDefaultValueDeserializers() +// .useValueDeserializer("newType", std::make_unique()) +// .build(); +// +// auto &casted = dynamic_cast(*parser); +// +// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); +// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +// EXPECT_EQ(casted.getDefaultType(), "string"); +// EXPECT_FALSE(casted.getAllowEmptyKeys()); +// EXPECT_TRUE(casted.getThrowOnUnknownType()); +// EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("newType")); +// EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("int")); +// EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("bool")); +// } +// +// TEST_F(SettingParserBuilderTest, ShouldUseCustomValueDeserializerMap) +// { +// sb::cf::SettingParserBuilder builder; +// +// auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); +// +// auto &casted = dynamic_cast(*parser); +// +// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); +// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +// EXPECT_EQ(casted.getDefaultType(), "string"); +// EXPECT_FALSE(casted.getAllowEmptyKeys()); +// EXPECT_TRUE(casted.getThrowOnUnknownType()); +// } +// +// TEST_F(SettingParserBuilderTest, ShouldUseCustomSplitter) +// { +// sb::cf::SettingParserBuilder builder; +// +// auto parser = builder.useSplitter(std::make_unique()).build(); +// +// auto &casted = dynamic_cast(*parser); +// +// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); +// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +// EXPECT_EQ(casted.getDefaultType(), "string"); +// EXPECT_FALSE(casted.getAllowEmptyKeys()); +// EXPECT_TRUE(casted.getThrowOnUnknownType()); +// } diff --git a/Tests/Unit/SettingParserTest.cpp b/Tests/Unit/SettingParserTest.cpp index c4eba41..a4dedc6 100644 --- a/Tests/Unit/SettingParserTest.cpp +++ b/Tests/Unit/SettingParserTest.cpp @@ -1,182 +1,184 @@ -#include -#include -#include - -#include "Mocks/DeserializerMock.hpp" -#include "Mocks/SettingSplitterMock.hpp" -#include "Mocks/ValueDeserializersMapMock.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - -class SettingParserTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - SettingParserTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -TEST_F(SettingParserTest, ShouldParseSetting) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!int=123"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = 123; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_FALSE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullSplitter) -{ - sb::cf::ISettingSplitter::Ptr splitter; - auto deserializers = std::make_unique(); - - EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, true}), - sb::cf::ConfigException); -} - -TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullDeserializers) -{ - sb::cf::IValueDeserializersMap::Ptr deserializers; - auto splitter = std::make_unique(); - - EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, true}), - sb::cf::ConfigException); -} - -TEST_F(SettingParserTest, ShouldUseDefaultType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = "value"; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); - EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); -} - -TEST_F(SettingParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--!string=value"; - sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = "value"; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueEmptyKey) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--!string=value"; - sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; - - EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_FALSE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldUseDefaultTypeForUnknownType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!unknown=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor) - .WillOnce(testing::Return(nullptr)) - .WillOnce(testing::Return(&deserializer)); - sb::cf::JsonValue returnedValue = "value"; - EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; - - EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_FALSE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailDueToForUnknownType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!unknown=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; - - EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_TRUE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} - -TEST_F(SettingParserTest, ShouldFailDueToForUnknownDefaultType) -{ - DeserializerMock deserializer; - auto deserializers = std::make_unique(); - auto splitter = std::make_unique(); - - std::string_view setting = "--option:deep:deep!unknown=value"; - sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; - EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); - EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); - sb::cf::JsonValue returnedValue = "value"; - - sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; - - EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); - EXPECT_EQ(parser.getDefaultType(), "string"); - EXPECT_FALSE(parser.getThrowOnUnknownType()); - EXPECT_TRUE(parser.getAllowEmptyKeys()); -} +// #include +// #include +// #include +// +// #include "Mocks/DeserializerMock.hpp" +// #include "Mocks/SettingSplitterMock.hpp" +// #include "Mocks/ValueDeserializersMapMock.hpp" +// #include "SevenBit/Conf/Details/SettingParser.hpp" +// #include "SevenBit/Conf/Exceptions.hpp" +// +// class SettingParserTest : public testing::Test +// { +// protected: +// static void TearUpTestSuite() {} +// +// SettingParserTest() {} +// +// void SetUp() override {} +// +// void TearDown() override {} +// +// static void TearDownTestSuite() {} +// }; +// +// TEST_F(SettingParserTest, ShouldParseSetting) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!int=123"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = 123; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_FALSE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullSplitter) +// { +// sb::cf::ISettingSplitter::Ptr splitter; +// auto deserializers = std::make_unique(); +// +// EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, +// true}), +// sb::cf::ConfigException); +// } +// +// TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullDeserializers) +// { +// sb::cf::IValueDeserializersMap::Ptr deserializers; +// auto splitter = std::make_unique(); +// +// EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, +// true}), +// sb::cf::ConfigException); +// } +// +// TEST_F(SettingParserTest, ShouldUseDefaultType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = "value"; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); +// EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); +// } +// +// TEST_F(SettingParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--!string=value"; +// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = "value"; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueEmptyKey) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--!string=value"; +// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; +// +// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_FALSE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(SettingParserTest, ShouldUseDefaultTypeForUnknownType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!unknown=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor) +// .WillOnce(testing::Return(nullptr)) +// .WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = "value"; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_FALSE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(SettingParserTest, ShouldFailDueToForUnknownType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!unknown=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; +// +// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(SettingParserTest, ShouldFailDueToForUnknownDefaultType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!unknown=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); +// sb::cf::JsonValue returnedValue = "value"; +// +// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; +// +// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_FALSE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } diff --git a/Tests/Unit/SettingSplitterTest.cpp b/Tests/Unit/SettingSplitterTest.cpp index be5a07a..8576a9b 100644 --- a/Tests/Unit/SettingSplitterTest.cpp +++ b/Tests/Unit/SettingSplitterTest.cpp @@ -1,114 +1,114 @@ -#include -#include - -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" -#include "Utilities/ParamsTest.hpp" - - -class SettingSplitterTest : public testing::Test -{ - protected: - static void TearUpTestSuite() {} - - SettingSplitterTest() {} - - void SetUp() override {} - - void TearDown() override {} - - static void TearDownTestSuite() {} -}; - -static Params SplitSettingData = { - {"--", {{""}}}, - {"one", {{"one"}}}, - {"--====", {{""}, std::nullopt, "==="}}, - {"--option!!type=val", {{"option!"}, "type", "val"}}, - {"--option!type=value", {{"option"}, "type", "value"}}, - {"//option___type;value", {{"option"}, "type", "value"}}, - {"--option=value", {{"option"}, std::nullopt, "value"}}, - {"--option=", {{"option"}, std::nullopt, ""}}, - {"--option!!type=", {{"option!"}, "type", ""}}, - {"--option!!type", {{"option!"}, "type"}}, - {"--option!!=type=val", {{"option!"}, "", "type=val"}}, - {"--option!!:inner!=type=val", {{"option!!", "inner"}, "", "type=val"}}, - {"--option!!:inner:::!=type=val", {{"option!!", "inner", "", "", ""}, "", "type=val"}}, - {":option:inner=type=val", {{"", "option", "inner"}, std::nullopt, "type=val"}}, - {"!!option:inner=type=val", {{"!"}, "option:inner", "type=val"}}, - {":option:inner=value", {{"", "option", "inner"}, std::nullopt, "value"}}, - {":option:inner=value::!", {{"", "option", "inner"}, std::nullopt, "value::!"}}, - {":option!!:inner=value::!", {{"", "option!"}, ":inner", "value::!"}}, - {":::!=value::!", {{"", "", "", ""}, "", "value::!"}}, - {"::!:=value::!", {{"", "", ""}, ":", "value::!"}}, - {":=:!:=value::!", {{"", ""}, std::nullopt, ":!:=value::!"}}, - {"__=:!___=value::!", {{"", ""}, std::nullopt, ":!___=value::!"}}, - {"_______=value", {{"", "", ""}, "", "value"}}, - {"__hello_____type=value", {{"", "hello", ""}, "type", "value"}}, - {"__hello__type=value", {{"", "hello", "type"}, std::nullopt, "value"}}, -}; -PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) -{ - const auto &[setting, expected] = GetParam(); - sb::cf::details::SettingSplitter splitter{{"--", "//"}, {"=", ";"}, {"!", "___"}, {":", "__"}}; - - EXPECT_EQ(splitter.split(setting), expected); -} - -static OneParams SettingSplittersData = {"=", ">", "===", "<<<<", "////"}; - -static OneParams SettingPrefixesData = {":", ";", "%%$", "***", "++"}; - -static OneParams KeySplittersData = {":", ";", "__", "{}", "\\"}; - -static OneParams TypeMarkersData = {"!", "@", ":", "[]", "{}"}; - -static Params, std::string, std::string> SplitSettingSimpleData = { - {{"key1", "key2", "key3"}, "string", "value"}, - {{"key1"}, "string", "value"}, - {{""}, "", ""}, - {{""}, "type", ""}, - {{"key"}, "", ""}, - {{""}, "", "value"}, -}; -PARAMS_TEST_COMBINED_5(SettingSplitterTest, ShouldSplitWithDifferentSplitters, SettingPrefixesData, - SettingSplittersData, TypeMarkersData, KeySplittersData, SplitSettingSimpleData) -{ - const auto &[prefix, settingSplitter, typeMarker, keySplitter, setting] = GetParam(); - const auto &[keys, type, value] = setting; - - sb::cf::details::SettingSplitter splitter{{prefix}, {settingSplitter}, {typeMarker}, {keySplitter}}; - - auto key = sb::cf::details::utils::joinViews(keys, keySplitter); - auto fullSetting = prefix + key + typeMarker + type + settingSplitter + value; - EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); -} - -static Params, std::vector, std::vector, - std::vector, sb::cf::ISettingSplitter::Result> - EmptySplittersData = { - {{}, {"=", ";"}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int", "value"}}, - {{"--", "//"}, {}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, - {{"--", "//"}, {"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, - {{"--", "//"}, {"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, - {{}, {}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int=value"}}, - {{}, {"=", ";"}, {}, {":", "__"}, {{"--key", "deep!int"}, std::nullopt, "value"}}, - {{}, {"=", ";"}, {"!", "___"}, {}, {{"--key:deep"}, "int", "value"}}, - {{"--", "//"}, {}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, - {{"--", "//"}, {}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, - {{"--", "//"}, {"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, - {{}, {}, {}, {":", "__"}, {{"--key", "deep!int=value"}}}, - {{}, {}, {"!", "___"}, {}, {{"--key:deep"}, "int=value"}}, - {{}, {"=", ";"}, {}, {}, {{"--key:deep!int"}, std::nullopt, "value"}}, - {{"--", "//"}, {}, {}, {}, {{"key:deep!int=value"}}}, - {{}, {}, {}, {}, {{"--key:deep!int=value"}}}, - -}; -PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersData) -{ - const auto &[prefixes, settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); - - sb::cf::details::SettingSplitter splitter{prefixes, settingSplitters, typeMarkers, keySplitters}; - - EXPECT_EQ(splitter.split("--key:deep!int=value"), expected); -} +// #include +// #include +// +// #include "SevenBit/Conf/Details/SettingSplitter.hpp" +// #include "SevenBit/Conf/Details/Utils.hpp" +// #include "Utilities/ParamsTest.hpp" +// +// +// class SettingSplitterTest : public testing::Test +// { +// protected: +// static void TearUpTestSuite() {} +// +// SettingSplitterTest() {} +// +// void SetUp() override {} +// +// void TearDown() override {} +// +// static void TearDownTestSuite() {} +// }; +// +// static Params SplitSettingData = { +// {"--", {{""}}}, +// {"one", {{"one"}}}, +// {"--====", {{""}, std::nullopt, "==="}}, +// {"--option!!type=val", {{"option!"}, "type", "val"}}, +// {"--option!type=value", {{"option"}, "type", "value"}}, +// {"//option___type;value", {{"option"}, "type", "value"}}, +// {"--option=value", {{"option"}, std::nullopt, "value"}}, +// {"--option=", {{"option"}, std::nullopt, ""}}, +// {"--option!!type=", {{"option!"}, "type", ""}}, +// {"--option!!type", {{"option!"}, "type"}}, +// {"--option!!=type=val", {{"option!"}, "", "type=val"}}, +// {"--option!!:inner!=type=val", {{"option!!", "inner"}, "", "type=val"}}, +// {"--option!!:inner:::!=type=val", {{"option!!", "inner", "", "", ""}, "", "type=val"}}, +// {":option:inner=type=val", {{"", "option", "inner"}, std::nullopt, "type=val"}}, +// {"!!option:inner=type=val", {{"!"}, "option:inner", "type=val"}}, +// {":option:inner=value", {{"", "option", "inner"}, std::nullopt, "value"}}, +// {":option:inner=value::!", {{"", "option", "inner"}, std::nullopt, "value::!"}}, +// {":option!!:inner=value::!", {{"", "option!"}, ":inner", "value::!"}}, +// {":::!=value::!", {{"", "", "", ""}, "", "value::!"}}, +// {"::!:=value::!", {{"", "", ""}, ":", "value::!"}}, +// {":=:!:=value::!", {{"", ""}, std::nullopt, ":!:=value::!"}}, +// {"__=:!___=value::!", {{"", ""}, std::nullopt, ":!___=value::!"}}, +// {"_______=value", {{"", "", ""}, "", "value"}}, +// {"__hello_____type=value", {{"", "hello", ""}, "type", "value"}}, +// {"__hello__type=value", {{"", "hello", "type"}, std::nullopt, "value"}}, +// }; +// PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) +// { +// const auto &[setting, expected] = GetParam(); +// sb::cf::details::SettingSplitter splitter{{"--", "//"}, {"=", ";"}, {"!", "___"}, {":", "__"}}; +// +// EXPECT_EQ(splitter.split(setting), expected); +// } +// +// static OneParams SettingSplittersData = {"=", ">", "===", "<<<<", "////"}; +// +// static OneParams SettingPrefixesData = {":", ";", "%%$", "***", "++"}; +// +// static OneParams KeySplittersData = {":", ";", "__", "{}", "\\"}; +// +// static OneParams TypeMarkersData = {"!", "@", ":", "[]", "{}"}; +// +// static Params, std::string, std::string> SplitSettingSimpleData = { +// {{"key1", "key2", "key3"}, "string", "value"}, +// {{"key1"}, "string", "value"}, +// {{""}, "", ""}, +// {{""}, "type", ""}, +// {{"key"}, "", ""}, +// {{""}, "", "value"}, +// }; +// PARAMS_TEST_COMBINED_5(SettingSplitterTest, ShouldSplitWithDifferentSplitters, SettingPrefixesData, +// SettingSplittersData, TypeMarkersData, KeySplittersData, SplitSettingSimpleData) +// { +// const auto &[prefix, settingSplitter, typeMarker, keySplitter, setting] = GetParam(); +// const auto &[keys, type, value] = setting; +// +// sb::cf::details::SettingSplitter splitter{{prefix}, {settingSplitter}, {typeMarker}, {keySplitter}}; +// +// auto key = sb::cf::details::utils::joinViews(keys, keySplitter); +// auto fullSetting = prefix + key + typeMarker + type + settingSplitter + value; +// EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); +// } +// +// static Params, std::vector, std::vector, +// std::vector, sb::cf::ISettingSplitter::Result> +// EmptySplittersData = { +// {{}, {"=", ";"}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int", "value"}}, +// {{"--", "//"}, {}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, +// {{"--", "//"}, {"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, +// {{"--", "//"}, {"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, +// {{}, {}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int=value"}}, +// {{}, {"=", ";"}, {}, {":", "__"}, {{"--key", "deep!int"}, std::nullopt, "value"}}, +// {{}, {"=", ";"}, {"!", "___"}, {}, {{"--key:deep"}, "int", "value"}}, +// {{"--", "//"}, {}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, +// {{"--", "//"}, {}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, +// {{"--", "//"}, {"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, +// {{}, {}, {}, {":", "__"}, {{"--key", "deep!int=value"}}}, +// {{}, {}, {"!", "___"}, {}, {{"--key:deep"}, "int=value"}}, +// {{}, {"=", ";"}, {}, {}, {{"--key:deep!int"}, std::nullopt, "value"}}, +// {{"--", "//"}, {}, {}, {}, {{"key:deep!int=value"}}}, +// {{}, {}, {}, {}, {{"--key:deep!int=value"}}}, +// +// }; +// PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersData) +// { +// const auto &[prefixes, settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); +// +// sb::cf::details::SettingSplitter splitter{prefixes, settingSplitters, typeMarkers, keySplitters}; +// +// EXPECT_EQ(splitter.split("--key:deep!int=value"), expected); +// } From 0d628e255c1e68e5ed90994d7917de3ac0fa9cf8 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Tue, 20 Feb 2024 23:08:38 +0100 Subject: [PATCH 05/31] add more e2e tests and refactor --- Examples/CustomSettingParser.cpp | 2 +- Examples/SettingParserConfig.cpp | 2 +- .../SevenBit/Conf/CommandLineParserConfig.hpp | 8 +- ...ttingsParser.hpp => CommandLineParser.hpp} | 24 ++--- .../Conf/Details/DefaultDeserializers.hpp | 3 - .../Conf/Details/EnvironmentVarsParser.hpp | 32 +++++++ .../Conf/Details/Impl/CommandLineParser.hpp | 84 +++++++++++++++++ .../Details/Impl/EnvironmentVarsParser.hpp | 37 ++++++++ .../Conf/Details/Impl/SettingParser.hpp | 38 -------- .../Conf/Details/Impl/SettingsParser.hpp | 80 ---------------- Include/SevenBit/Conf/Details/Impl/Utils.hpp | 31 ++++--- .../SevenBit/Conf/Details/SettingParser.hpp | 36 -------- .../SevenBit/Conf/Details/SettingSplitter.hpp | 4 +- Include/SevenBit/Conf/Details/Utils.hpp | 18 +--- .../Conf/EnvironmentVarsParserBuilder.hpp | 51 +++++++++++ ...ig.hpp => EnvironmentVarsParserConfig.hpp} | 9 +- .../SevenBit/Conf/IConfigurationBuilder.hpp | 44 +++++++-- Include/SevenBit/Conf/ISettingParser.hpp | 32 ------- .../Conf/Impl/CommandLineParserBuilder.hpp | 13 ++- .../Impl/EnvironmentVarsParserBuilder.hpp | 83 +++++++++++++++++ .../Conf/Impl/SettingParserBuilder.hpp | 91 ------------------- .../SevenBit/Conf/SettingParserBuilder.hpp | 51 ----------- .../Conf/Sources/CommandLineConfiguration.hpp | 1 - .../Sources/EnvironmentVarsConfiguration.hpp | 11 +-- .../Impl/EnvironmentVarsConfiguration.hpp | 14 +-- README.md | 16 ++-- Tests/CMakeLists.txt | 21 ++--- Tests/EndToEnd/CMakeLists.txt | 13 ++- Tests/EndToEnd/Cli/echo.cpp | 13 +++ Tests/EndToEnd/Cli/test.py | 59 ++++++++++++ Tests/EndToEnd/Cli/testData.json | 39 ++++++++ Tests/EndToEnd/Examples/expectedTestData.json | 14 --- Tests/EndToEnd/Examples/test_examples.py | 69 -------------- Tests/Unit/CMakeLists.txt | 10 +- Tests/Unit/SettingParserBuilderTest.cpp | 6 +- Tests/Unit/SettingParserTest.cpp | 2 +- 36 files changed, 529 insertions(+), 532 deletions(-) rename Include/SevenBit/Conf/Details/{SettingsParser.hpp => CommandLineParser.hpp} (50%) create mode 100644 Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp create mode 100644 Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp create mode 100644 Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp delete mode 100644 Include/SevenBit/Conf/Details/Impl/SettingParser.hpp delete mode 100644 Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp delete mode 100644 Include/SevenBit/Conf/Details/SettingParser.hpp create mode 100644 Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp rename Include/SevenBit/Conf/{Details/SettingParserConfig.hpp => EnvironmentVarsParserConfig.hpp} (53%) delete mode 100644 Include/SevenBit/Conf/ISettingParser.hpp create mode 100644 Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp delete mode 100644 Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp delete mode 100644 Include/SevenBit/Conf/SettingParserBuilder.hpp create mode 100644 Tests/EndToEnd/Cli/echo.cpp create mode 100644 Tests/EndToEnd/Cli/test.py create mode 100644 Tests/EndToEnd/Cli/testData.json delete mode 100644 Tests/EndToEnd/Examples/expectedTestData.json delete mode 100644 Tests/EndToEnd/Examples/test_examples.py diff --git a/Examples/CustomSettingParser.cpp b/Examples/CustomSettingParser.cpp index 9c122e9..b463691 100644 --- a/Examples/CustomSettingParser.cpp +++ b/Examples/CustomSettingParser.cpp @@ -10,7 +10,7 @@ struct MyTypeDeserializer final : IDeserializer int main(int argc, char **argv) { - SettingParserConfig envParserConfig; + EnvironmentVarsParserConfig envParserConfig; envParserConfig.keySplitters.clear(); envParserConfig.settingPrefixes.emplace_back("//"); envParserConfig.defaultType = "myType"; diff --git a/Examples/SettingParserConfig.cpp b/Examples/SettingParserConfig.cpp index 143e45f..39f74cf 100644 --- a/Examples/SettingParserConfig.cpp +++ b/Examples/SettingParserConfig.cpp @@ -5,7 +5,7 @@ using namespace sb::cf; int main(int argc, char **argv) { - SettingParserConfig envParserConfig; + EnvironmentVarsParserConfig envParserConfig; envParserConfig.keySplitters.clear(); envParserConfig.typeMarkers.clear(); diff --git a/Include/SevenBit/Conf/CommandLineParserConfig.hpp b/Include/SevenBit/Conf/CommandLineParserConfig.hpp index 66c9ad0..1038c26 100644 --- a/Include/SevenBit/Conf/CommandLineParserConfig.hpp +++ b/Include/SevenBit/Conf/CommandLineParserConfig.hpp @@ -9,10 +9,10 @@ namespace sb::cf { struct CommandLineParserConfig { - std::vector optionPrefixes = {"--", "/"}; - std::vector optionSplitters = {"=", " "}; - std::vector optionKeySplitters = {":"}; - std::vector optionTypeMarkers = {"!"}; + std::vector prefixes = {"--", "/"}; + std::vector splitters = {"=", " "}; + std::vector keySplitters = {":"}; + std::vector typeMarkers = {"!"}; std::string_view defaultType = "string"; bool throwOnUnknownType = true; bool allowEmptyKeys = false; diff --git a/Include/SevenBit/Conf/Details/SettingsParser.hpp b/Include/SevenBit/Conf/Details/CommandLineParser.hpp similarity index 50% rename from Include/SevenBit/Conf/Details/SettingsParser.hpp rename to Include/SevenBit/Conf/Details/CommandLineParser.hpp index 7b98323..22e7e7d 100644 --- a/Include/SevenBit/Conf/Details/SettingsParser.hpp +++ b/Include/SevenBit/Conf/Details/CommandLineParser.hpp @@ -12,7 +12,7 @@ namespace sb::cf::details { - class EXPORT SettingsParser : public ISettingsParser + class EXPORT CommandLineParser : public ISettingsParser { struct ArgumentParseResult { @@ -20,29 +20,29 @@ namespace sb::cf::details JsonValue value; }; - const ISettingSplitter::Ptr _settingSplitter; + const ISettingSplitter::Ptr _optionSplitter; const IValueDeserializersMap::Ptr _valueDeserializersMap; - const std::vector _settingPrefixes; - bool _combinedSetting; + const std::vector _optionPrefixes; + const bool _considerSeparated; public: - using Ptr = std::unique_ptr; + using Ptr = std::unique_ptr; - SettingsParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap, - std::vector settingPrefixes, bool combinedSetting); + CommandLineParser(ISettingSplitter::Ptr optionSplitter, IValueDeserializersMap::Ptr valueDeserializersMap, + std::vector optionPrefixes, bool considerSeparated); - [[nodiscard]] JsonObject parse(const std::vector &settings) const override; + [[nodiscard]] JsonObject parse(const std::vector &arguments) const override; private: - [[nodiscard]] ArgumentParseResult parseArgument(const std::vector &settings, + [[nodiscard]] ArgumentParseResult parseArgument(const std::vector &arguments, size_t &index) const; - bool tryRemovePrefix(std::string_view &setting) const; + bool tryRemoveOptionPrefix(std::string_view &argument) const; - [[nodiscard]] std::optional tryGetPrefix(std::string_view setting) const; + [[nodiscard]] std::optional tryGetOptionPrefix(std::string_view argument) const; }; } // namespace sb::cf::details #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/SettingsParser.hpp" +#include "SevenBit/Conf/Details/Impl/CommandLineParser.hpp" #endif diff --git a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp index 74c8696..b1d95f6 100644 --- a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp +++ b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp @@ -1,9 +1,6 @@ #pragma once #include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/SettingsParser.hpp" namespace sb::cf { diff --git a/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp new file mode 100644 index 0000000..1663877 --- /dev/null +++ b/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/ISettingSplitter.hpp" +#include "SevenBit/Conf/ISettingsParser.hpp" +#include "SevenBit/Conf/IValueDeserializersMap.hpp" + +namespace sb::cf::details +{ + class EXPORT EnvironmentVarsParser : public ISettingsParser + { + private: + const ISettingSplitter::Ptr _settingSplitter; + const IValueDeserializersMap::Ptr _valueDeserializersMap; + + public: + using Ptr = std::unique_ptr; + + EnvironmentVarsParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap); + + [[nodiscard]] JsonObject parse(const std::vector &envVariables) const override; + }; +} // namespace sb::cf::details + +#ifdef _7BIT_CONF_ADD_IMPL +#include "SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp" +#endif diff --git a/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp b/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp new file mode 100644 index 0000000..159468f --- /dev/null +++ b/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include "SevenBit/Conf/Details/CommandLineParser.hpp" +#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Exceptions.hpp" + +#include + +namespace sb::cf::details +{ + INLINE CommandLineParser::CommandLineParser(ISettingSplitter::Ptr optionSplitter, + IValueDeserializersMap::Ptr valueDeserializersMap, + std::vector optionPrefixes, + const bool considerSeparated) + : _optionSplitter(std::move(optionSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)), + _optionPrefixes(std::move(optionPrefixes)), _considerSeparated(considerSeparated) + { + utils::assertPtr(_optionSplitter); + utils::assertPtr(_valueDeserializersMap); + } + + INLINE JsonObject CommandLineParser::parse(const std::vector &arguments) const + { + JsonObject result; + for (size_t index = 0; index < arguments.size(); ++index) + { + try + { + auto [keys, value] = parseArgument(arguments, index); + JsonExt::updateWith(result, keys, std::move(value)); + } + catch (const std::exception &e) + { + throw ConfigException("Parsing error for argument '" + std::string{arguments[index]} + + "' error: " + e.what()); + } + } + return result; + } + + INLINE CommandLineParser::ArgumentParseResult CommandLineParser::parseArgument( + const std::vector &arguments, size_t &index) const + { + auto argument = arguments[index]; + const auto hasOptionPrefix = tryRemoveOptionPrefix(argument); + + auto [keys, type, value] = _optionSplitter->split(argument); + + if (_considerSeparated && hasOptionPrefix && !value && index + 1 < arguments.size()) + { + if (auto nextArgument = arguments[index + 1]; !tryGetOptionPrefix(nextArgument)) + { + value = nextArgument; + ++index; + } + } + return {keys, _valueDeserializersMap->getDeserializerFor(type).deserialize(value)}; + } + + INLINE bool CommandLineParser::tryRemoveOptionPrefix(std::string_view &argument) const + { + if (const auto optionPrefix = tryGetOptionPrefix(argument)) + { + argument.remove_prefix(optionPrefix->size()); + return true; + } + return false; + } + + INLINE std::optional CommandLineParser::tryGetOptionPrefix(std::string_view argument) const + { + for (auto &optionPrefix : _optionPrefixes) + { + if (utils::startsWith(argument, optionPrefix)) + { + return optionPrefix; + } + } + return std::nullopt; + } + +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp new file mode 100644 index 0000000..3c3b1f0 --- /dev/null +++ b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" +#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Exceptions.hpp" + +namespace sb::cf::details +{ + INLINE EnvironmentVarsParser::EnvironmentVarsParser(ISettingSplitter::Ptr settingSplitter, + IValueDeserializersMap::Ptr valueDeserializersMap) + : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)) + { + utils::assertPtr(_settingSplitter); + utils::assertPtr(_valueDeserializersMap); + } + + INLINE JsonObject EnvironmentVarsParser::parse(const std::vector &envVariables) const + { + JsonObject result; + for (const auto variable : envVariables) + { + try + { + auto [keys, type, value] = _settingSplitter->split(variable); + JsonExt::updateWith(result, keys, _valueDeserializersMap->getDeserializerFor(type).deserialize(value)); + } + catch (const std::exception &e) + { + throw SettingParserException("Parsing error for environment variable '" + std::string{variable} + + "' error: " + e.what()); + } + } + return result; + } +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp b/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp deleted file mode 100644 index a5d03de..0000000 --- a/Include/SevenBit/Conf/Details/Impl/SettingParser.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - -namespace sb::cf::details -{ - INLINE SettingParser::SettingParser(ISettingSplitter::Ptr settingSplitter, - IValueDeserializersMap::Ptr valueDeserializersMap) - : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)) - { - utils::assertPtr(_settingSplitter); - utils::assertPtr(_valueDeserializersMap); - } - - INLINE ISettingParser::Result SettingParser::parse(std::string_view setting) const - { - try - { - auto [keys, type, value] = getSettingSplitter().split(setting); - return {std::move(keys), getValueDeserializersMap().getDeserializerFor(type).deserialize(value)}; - } - catch (const std::exception &e) - { - throw SettingParserException("Parsing error for setting '" + std::string{setting} + "' error: " + e.what()); - } - } - - INLINE const ISettingSplitter &SettingParser::getSettingSplitter() const { return *_settingSplitter; } - - INLINE const IValueDeserializersMap &SettingParser::getValueDeserializersMap() const - { - return *_valueDeserializersMap; - } -} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp b/Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp deleted file mode 100644 index cd22d4b..0000000 --- a/Include/SevenBit/Conf/Details/Impl/SettingsParser.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include "SevenBit/Conf/Details/SettingsParser.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - -#include - -namespace sb::cf::details -{ - INLINE SettingsParser::SettingsParser(ISettingSplitter::Ptr settingSplitter, - IValueDeserializersMap::Ptr valueDeserializersMap, - std::vector settingPrefixes, bool combinedSetting) - : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)), - _settingPrefixes(std::move(settingPrefixes)), _combinedSetting(combinedSetting) - { - utils::assertPtr(_settingSplitter); - utils::assertPtr(_valueDeserializersMap); - } - - INLINE JsonObject SettingsParser::parse(const std::vector &settings) const - { - JsonObject result; - for (size_t index = 0; index < settings.size(); ++index) - { - try - { - auto [keys, value] = parseArgument(settings, index); - JsonExt::updateWith(result, keys, std::move(value)); - } - catch (const std::exception &e) - { - throw SettingParserException("Parsing error for setting '" + std::string{settings[index]} + - "' error: " + e.what()); - } - } - return result; - } - - INLINE SettingsParser::ArgumentParseResult SettingsParser::parseArgument( - const std::vector &settings, size_t &index) const - { - auto setting = settings[index]; - const auto hasOptionPrefix = tryRemovePrefix(setting); - - auto [keys, type, value] = _settingSplitter->split(setting); - - if (_combinedSetting && hasOptionPrefix && !value && index + 1 < settings.size() && - !tryGetPrefix(settings[index + 1])) - { - value = settings[++index]; - } - return {keys, _valueDeserializersMap->getDeserializerFor(type).deserialize(value)}; - } - - INLINE bool SettingsParser::tryRemovePrefix(std::string_view &setting) const - { - if (const auto settingPrefix = tryGetPrefix(setting)) - { - setting.remove_prefix(settingPrefix->size()); - return true; - } - return false; - } - - INLINE std::optional SettingsParser::tryGetPrefix(std::string_view setting) const - { - for (auto &settingPrefix : _settingPrefixes) - { - if (utils::startsWith(setting, settingPrefix)) - { - return settingPrefix; - } - } - return std::nullopt; - } - -} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/Utils.hpp b/Include/SevenBit/Conf/Details/Impl/Utils.hpp index a9df582..cd6782b 100644 --- a/Include/SevenBit/Conf/Details/Impl/Utils.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Utils.hpp @@ -10,20 +10,21 @@ namespace sb::cf::details::utils { INLINE bool isNumberString(std::string_view str) { - return !str.empty() && std::all_of(str.begin(), str.end(), [](char ch) { return std::isdigit(ch); }); + return !str.empty() && std::all_of(str.begin(), str.end(), [](unsigned char ch) { return std::isdigit(ch); }); } INLINE bool ignoreCaseLess(std::string_view str, std::string_view search) { - return std::lexicographical_compare(str.begin(), str.end(), search.begin(), search.end(), - [](char cha, char chb) { return std::tolower(cha) < std::tolower(chb); }); + return std::lexicographical_compare( + str.begin(), str.end(), search.begin(), search.end(), + [](unsigned char cha, unsigned char chb) { return std::tolower(cha) < std::tolower(chb); }); } INLINE bool ignoreCaseEqual(std::string_view str, std::string_view search) { return str.size() == search.size() && std::equal(str.begin(), str.end(), search.begin(), search.end(), - [](char cha, char chb) { return std::tolower(cha) == std::tolower(chb); }); + [](unsigned char cha, unsigned char chb) { return std::tolower(cha) == std::tolower(chb); }); } INLINE bool containsAt(std::string_view str, size_t index, std::string_view search) @@ -140,15 +141,23 @@ namespace sb::cf::details::utils INLINE std::string joinViews(const std::vector &strings, const std::string ÷r) { std::string res; - if (strings.empty()) + if (!strings.empty()) { - return res; - } - for (size_t i = 0; i < strings.size() - 1; ++i) - { - res += std::string{strings[i]} + divider; + for (size_t i = 0; i < strings.size() - 1; ++i) + { + res += std::string{strings[i]} + divider; + } + res += strings.back(); } - res += strings.back(); return res; } + + INLINE inline size_t startsWhiteSpace(std::string_view str) + { + const auto it = std::find_if(str.begin(), str.end(), [](const unsigned char ch) { return !std::isspace(ch); }); + return it - str.begin(); + } + + INLINE inline bool isWhiteSpace(std::string_view str) { return startsWhiteSpace(str) == str.size(); } + } // namespace sb::cf::details::utils diff --git a/Include/SevenBit/Conf/Details/SettingParser.hpp b/Include/SevenBit/Conf/Details/SettingParser.hpp deleted file mode 100644 index 404f43e..0000000 --- a/Include/SevenBit/Conf/Details/SettingParser.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "SevenBit/Conf/LibraryConfig.hpp" - -#include "SevenBit/Conf/ISettingParser.hpp" -#include "SevenBit/Conf/ISettingSplitter.hpp" -#include "SevenBit/Conf/IValueDeserializersMap.hpp" - -namespace sb::cf::details -{ - class EXPORT SettingParser : public ISettingParser - { - private: - const ISettingSplitter::Ptr _settingSplitter; - const IValueDeserializersMap::Ptr _valueDeserializersMap; - - public: - using Ptr = std::unique_ptr; - - SettingParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap); - - [[nodiscard]] ISettingParser::Result parse(std::string_view setting) const override; - - [[nodiscard]] const ISettingSplitter &getSettingSplitter() const; - - [[nodiscard]] const IValueDeserializersMap &getValueDeserializersMap() const; - }; -} // namespace sb::cf::details - -#ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/SettingParser.hpp" -#endif diff --git a/Include/SevenBit/Conf/Details/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/SettingSplitter.hpp index 4324d23..8de3dc2 100644 --- a/Include/SevenBit/Conf/Details/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/SettingSplitter.hpp @@ -22,9 +22,9 @@ namespace sb::cf::details public: SettingSplitter(std::vector settingSplitters, std::vector typeMarkers, - std::vector keySplitters, bool allowEmptyKeys); + std::vector keySplitters, bool allowEmptyKeys = false); - [[nodiscard]] ISettingSplitter::Result split(std::string_view setting) const override; + [[nodiscard]] Result split(std::string_view setting) const override; private: [[nodiscard]] std::pair> splitSetting( diff --git a/Include/SevenBit/Conf/Details/Utils.hpp b/Include/SevenBit/Conf/Details/Utils.hpp index e068e7b..45c945a 100644 --- a/Include/SevenBit/Conf/Details/Utils.hpp +++ b/Include/SevenBit/Conf/Details/Utils.hpp @@ -2,11 +2,9 @@ #include #include -#include #include #include #include -#include #include #include "SevenBit/Conf/LibraryConfig.hpp" @@ -57,21 +55,9 @@ namespace sb::cf::details::utils template void assertPtr(const std::shared_ptr &ptr) { assertPtr(ptr.get()); } - EXPORT inline size_t startsWhiteSpace(std::string_view str) - { - size_t result = 0; - for (unsigned char ch : str) - { - if (!std::isspace(ch)) - { - break; - } - ++result; - } - return result; - } + EXPORT inline size_t startsWhiteSpace(std::string_view str); - EXPORT inline bool isWhiteSpace(std::string_view str) { return startsWhiteSpace(str) == str.size(); } + EXPORT inline bool isWhiteSpace(std::string_view str); template std::pair tryStringTo(std::string_view str, bool full = true) { diff --git a/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp b/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp new file mode 100644 index 0000000..cf0eac6 --- /dev/null +++ b/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/EnvironmentVarsParserConfig.hpp" +#include "SevenBit/Conf/ISettingSplitter.hpp" +#include "SevenBit/Conf/ISettingsParser.hpp" +#include "SevenBit/Conf/IValueDeserializersMap.hpp" + +namespace sb::cf +{ + + class EXPORT EnvironmentVarsParserBuilder + { + private: + ISettingSplitter::Ptr _splitter; + IValueDeserializersMap::Ptr _valueDeserializersMap; + std::vector> _valueDeserializers; + std::optional _config; + + public: + EnvironmentVarsParserBuilder &useSplitter(ISettingSplitter::Ptr splitter); + + EnvironmentVarsParserBuilder &useValueDeserializersMap(IValueDeserializersMap::Ptr valueDeserializersMap); + + EnvironmentVarsParserBuilder &useValueDeserializer(std::string_view type, IDeserializer::Ptr valueDeserializer); + + EnvironmentVarsParserBuilder &useConfig(EnvironmentVarsParserConfig config); + + EnvironmentVarsParserBuilder &useDefaultValueDeserializers(); + + ISettingsParser::Ptr build(); + + private: + ISettingSplitter::Ptr getSplitter(); + + IValueDeserializersMap::Ptr getValueDeserializersMap(); + + EnvironmentVarsParserConfig &getConfig(); + }; + +} // namespace sb::cf + +#ifdef _7BIT_CONF_ADD_IMPL +#include "SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp" +#endif diff --git a/Include/SevenBit/Conf/Details/SettingParserConfig.hpp b/Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp similarity index 53% rename from Include/SevenBit/Conf/Details/SettingParserConfig.hpp rename to Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp index 4dc8528..ab5fe70 100644 --- a/Include/SevenBit/Conf/Details/SettingParserConfig.hpp +++ b/Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp @@ -7,12 +7,11 @@ namespace sb::cf { - struct SettingParserConfig + struct EnvironmentVarsParserConfig { - std::vector settingPrefixes; - std::vector settingSplitters; - std::vector keySplitters; - std::vector typeMarkers; + std::vector splitters = {"="}; + std::vector keySplitters = {":", "__"}; + std::vector typeMarkers = {"!", "___"}; std::string_view defaultType = "string"; bool throwOnUnknownType = true; bool allowEmptyKeys = false; diff --git a/Include/SevenBit/Conf/IConfigurationBuilder.hpp b/Include/SevenBit/Conf/IConfigurationBuilder.hpp index b8d830b..2996d36 100644 --- a/Include/SevenBit/Conf/IConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/IConfigurationBuilder.hpp @@ -12,10 +12,9 @@ #include "SevenBit/Conf/LibraryConfig.hpp" #include "SevenBit/Conf/CommandLineParserBuilder.hpp" -#include "SevenBit/Conf/CommandLineParserConfig.hpp" +#include "SevenBit/Conf/EnvironmentVarsParserBuilder.hpp" #include "SevenBit/Conf/IConfiguration.hpp" #include "SevenBit/Conf/IObject.hpp" -#include "SevenBit/Conf/SettingParserBuilder.hpp" #include "SevenBit/Conf/Sources/AppSettingsConfiguration.hpp" #include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" #include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" @@ -69,20 +68,31 @@ namespace sb::cf return add(AppSettingsConfigurationSource::create(std::move(environmentName))); } - IConfigurationBuilder &addEnvironmentVariables() { return addEnvironmentVariables("", SettingParserConfig{}); } + IConfigurationBuilder &addEnvironmentVariables() + { + return addEnvironmentVariables("", EnvironmentVarsParserConfig{}); + } IConfigurationBuilder &addEnvironmentVariables(std::string prefix) { - return addEnvironmentVariables(std::move(prefix), SettingParserConfig{}); + return addEnvironmentVariables(std::move(prefix), EnvironmentVarsParserConfig{}); } - IConfigurationBuilder &addEnvironmentVariables(std::string prefix, SettingParserConfig config) + IConfigurationBuilder &addEnvironmentVariables(std::string prefix, EnvironmentVarsParserConfig config) { return addEnvironmentVariables(std::move(prefix), - SettingParserBuilder{}.useConfig(std::move(config)).build()); + [&](auto &builder) { builder.useConfig(std::move(config)); }); + } + + template + IConfigurationBuilder &addEnvironmentVariables(std::string prefix, TBuilderFunc parserBuilderFunctor) + { + EnvironmentVarsParserBuilder builder; + parserBuilderFunctor(builder); + return addEnvironmentVariables(std::move(prefix), builder.build()); } - IConfigurationBuilder &addEnvironmentVariables(std::string prefix, ISettingParser::Ptr parser) + IConfigurationBuilder &addEnvironmentVariables(std::string prefix, ISettingsParser::Ptr parser) { return add(EnvironmentVarsConfigurationSource::create(std::move(prefix), std::move(parser))); } @@ -99,12 +109,28 @@ namespace sb::cf IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, CommandLineParserConfig config) { - return addCommandLine(argc, argv, CommandLineParserBuilder{}.useConfig(std::move(config)).build()); + return addCommandLine(argc, argv, [&](auto &builder) { builder.useConfig(std::move(config)); }); } IConfigurationBuilder &addCommandLine(std::vector args, CommandLineParserConfig config) { - return addCommandLine(std::move(args), CommandLineParserBuilder{}.useConfig(std::move(config)).build()); + return addCommandLine(std::move(args), [&](auto &builder) { builder.useConfig(std::move(config)); }); + } + + template + IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, TBuilderFunc parserBuilderFunctor) + { + CommandLineParserBuilder builder; + parserBuilderFunctor(builder); + return addCommandLine(argc, argv, builder.build()); + } + + template + IConfigurationBuilder &addCommandLine(std::vector args, TBuilderFunc parserBuilderFunctor) + { + CommandLineParserBuilder builder; + parserBuilderFunctor(builder); + return addCommandLine(std::move(args), builder.build()); } IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, ISettingsParser::Ptr parser) diff --git a/Include/SevenBit/Conf/ISettingParser.hpp b/Include/SevenBit/Conf/ISettingParser.hpp deleted file mode 100644 index 8f7f8d1..0000000 --- a/Include/SevenBit/Conf/ISettingParser.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "SevenBit/Conf/LibraryConfig.hpp" - -#include "SevenBit/Conf/Json.hpp" - -namespace sb::cf -{ - struct ISettingParser - { - using Ptr = std::unique_ptr; - - struct Result - { - std::vector keys; - JsonValue value; - }; - - [[nodiscard]] virtual Result parse(std::string_view setting) const = 0; - - virtual ~ISettingParser() = default; - }; - - inline bool operator==(const ISettingParser::Result &lhs, const ISettingParser::Result &rhs) - { - return lhs.keys == rhs.keys && lhs.value == rhs.value; - } -} // namespace sb::cf diff --git a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp index 605a5b2..5107f00 100644 --- a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp @@ -1,11 +1,10 @@ #pragma once #include "SevenBit/Conf/CommandLineParserBuilder.hpp" +#include "SevenBit/Conf/Details/CommandLineParser.hpp" #include "SevenBit/Conf/Details/DefaultDeserializers.hpp" #include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" #include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/SettingsParser.hpp" #include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" @@ -46,13 +45,13 @@ namespace sb::cf INLINE ISettingsParser::Ptr CommandLineParserBuilder::build() { auto hadWhiteSpaceSplitters = tryRemoveWhiteSpaceSplitters(); - return std::make_unique(getSplitter(), getValueDeserializersMap(), - std::move(getConfig().optionPrefixes), hadWhiteSpaceSplitters); + return std::make_unique( + getSplitter(), getValueDeserializersMap(), std::move(getConfig().prefixes), hadWhiteSpaceSplitters); } INLINE bool CommandLineParserBuilder::tryRemoveWhiteSpaceSplitters() { - auto &splitters = getConfig().optionSplitters; + auto &splitters = getConfig().splitters; const auto it = details::utils::removeIf(splitters.begin(), splitters.end(), [](auto splitter) { return details::utils::isWhiteSpace(splitter); }); const auto removeCnt = splitters.end() - it; @@ -66,8 +65,8 @@ namespace sb::cf { auto &config = getConfig(); useSplitter(std::make_unique( - std::move(config.optionSplitters), std::move(config.optionTypeMarkers), - std::move(config.optionKeySplitters), config.allowEmptyKeys)); + std::move(config.splitters), std::move(config.typeMarkers), + std::move(config.keySplitters), config.allowEmptyKeys)); } return std::move(_splitter); } diff --git a/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp b/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp new file mode 100644 index 0000000..3836eba --- /dev/null +++ b/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include "SevenBit/Conf/Details/DefaultDeserializers.hpp" +#include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +#include "SevenBit/Conf/EnvironmentVarsParserBuilder.hpp" + +namespace sb::cf +{ + INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useSplitter(ISettingSplitter::Ptr splitter) + { + _splitter = std::move(splitter); + return *this; + } + + INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useValueDeserializersMap( + IValueDeserializersMap::Ptr valueDeserializersMap) + { + _valueDeserializersMap = std::move(valueDeserializersMap); + return *this; + } + + INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useValueDeserializer( + std::string_view type, IDeserializer::Ptr valueDeserializer) + { + _valueDeserializers.emplace_back(type, std::move(valueDeserializer)); + return *this; + } + + INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useConfig(EnvironmentVarsParserConfig config) + { + _config = std::move(config); + return *this; + } + + INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useDefaultValueDeserializers() + { + DefaultDeserializers::addDefault(_valueDeserializers); + return *this; + } + + INLINE ISettingsParser::Ptr EnvironmentVarsParserBuilder::build() + { + return std::make_unique(getSplitter(), getValueDeserializersMap()); + } + + INLINE ISettingSplitter::Ptr EnvironmentVarsParserBuilder::getSplitter() + { + if (!_splitter) + { + auto &config = getConfig(); + useSplitter( + std::make_unique(std::move(config.splitters), std::move(config.typeMarkers), + std::move(config.keySplitters), config.allowEmptyKeys)); + } + return std::move(_splitter); + } + + INLINE IValueDeserializersMap::Ptr EnvironmentVarsParserBuilder::getValueDeserializersMap() + { + if (!_valueDeserializersMap) + { + if (_valueDeserializers.empty()) + { + useDefaultValueDeserializers(); + } + auto &config = getConfig(); + useValueDeserializersMap(std::make_unique( + config.defaultType, config.throwOnUnknownType, std::move(_valueDeserializers))); + } + return std::move(_valueDeserializersMap); + } + + INLINE EnvironmentVarsParserConfig &EnvironmentVarsParserBuilder::getConfig() + { + if (!_config) + { + useConfig({}); + } + return *_config; + } +} // namespace sb::cf diff --git a/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp b/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp deleted file mode 100644 index a4d020e..0000000 --- a/Include/SevenBit/Conf/Impl/SettingParserBuilder.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/SettingParser.hpp" -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/SettingsParser.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -#include "SevenBit/Conf/SettingParserBuilder.hpp" - -namespace sb::cf -{ - INLINE SettingParserBuilder &SettingParserBuilder::useSplitter(ISettingSplitter::Ptr splitter) - { - _splitter = std::move(splitter); - return *this; - } - - INLINE SettingParserBuilder &SettingParserBuilder::useValueDeserializersMap( - IValueDeserializersMap::Ptr valueDeserializersMap) - { - _valueDeserializersMap = std::move(valueDeserializersMap); - return *this; - } - - INLINE SettingParserBuilder &SettingParserBuilder::useValueDeserializer(std::string_view type, - IDeserializer::Ptr valueDeserializer) - { - _valueDeserializers.emplace_back(type, std::move(valueDeserializer)); - return *this; - } - - INLINE SettingParserBuilder &SettingParserBuilder::useConfig(SettingParserConfig config) - { - _config = std::move(config); - return *this; - } - - INLINE SettingParserBuilder &SettingParserBuilder::useDefaultValueDeserializers() - { - useValueDeserializer("string", std::make_unique()); - useValueDeserializer("bool", std::make_unique()); - useValueDeserializer("int", std::make_unique()); - useValueDeserializer("double", std::make_unique()); - useValueDeserializer("uint", std::make_unique()); - useValueDeserializer("json", std::make_unique()); - useValueDeserializer("null", std::make_unique()); - return *this; - } - - INLINE ISettingParser::Ptr SettingParserBuilder::build() - { - return std::make_unique(getSplitter(), getValueDeserializersMap()); - } - - INLINE ISettingSplitter::Ptr SettingParserBuilder::getSplitter() - { - if (!_splitter) - { - auto &config = getConfig(); - useSplitter(std::make_unique( - std::move(config.settingSplitters), std::move(config.typeMarkers), std::move(config.keySplitters), - config.allowEmptyKeys)); - } - return std::move(_splitter); - } - - INLINE IValueDeserializersMap::Ptr SettingParserBuilder::getValueDeserializersMap() - { - if (!_valueDeserializersMap) - { - if (_valueDeserializers.empty()) - { - useDefaultValueDeserializers(); - } - auto &config = getConfig(); - useValueDeserializersMap(std::make_unique( - config.defaultType, config.throwOnUnknownType, std::move(_valueDeserializers))); - } - return std::move(_valueDeserializersMap); - } - - INLINE SettingParserConfig &SettingParserBuilder::getConfig() - { - if (!_config) - { - useConfig({}); - } - return *_config; - } -} // namespace sb::cf diff --git a/Include/SevenBit/Conf/SettingParserBuilder.hpp b/Include/SevenBit/Conf/SettingParserBuilder.hpp deleted file mode 100644 index 39e8135..0000000 --- a/Include/SevenBit/Conf/SettingParserBuilder.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "SevenBit/Conf/LibraryConfig.hpp" - -#include "SevenBit/Conf/Details/SettingParserConfig.hpp" -#include "SevenBit/Conf/ISettingParser.hpp" -#include "SevenBit/Conf/ISettingSplitter.hpp" -#include "SevenBit/Conf/IValueDeserializersMap.hpp" - -namespace sb::cf -{ - - class EXPORT SettingParserBuilder - { - private: - ISettingSplitter::Ptr _splitter; - IValueDeserializersMap::Ptr _valueDeserializersMap; - std::vector> _valueDeserializers; - std::optional _config; - - public: - SettingParserBuilder &useSplitter(ISettingSplitter::Ptr splitter); - - SettingParserBuilder &useValueDeserializersMap(IValueDeserializersMap::Ptr valueDeserializersMap); - - SettingParserBuilder &useValueDeserializer(std::string_view type, IDeserializer::Ptr valueDeserializer); - - SettingParserBuilder &useConfig(SettingParserConfig config); - - SettingParserBuilder &useDefaultValueDeserializers(); - - ISettingParser::Ptr build(); - - private: - ISettingSplitter::Ptr getSplitter(); - - IValueDeserializersMap::Ptr getValueDeserializersMap(); - - SettingParserConfig &getConfig(); - }; - -} // namespace sb::cf - -#ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Impl/SettingParserBuilder.hpp" -#endif diff --git a/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp index 05fb290..1941287 100644 --- a/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/CommandLineConfiguration.hpp @@ -5,7 +5,6 @@ #include "SevenBit/Conf/LibraryConfig.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" -#include "SevenBit/Conf/SettingParserBuilder.hpp" #include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp index 91c4cb1..d3601b2 100644 --- a/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp @@ -5,9 +5,8 @@ #include "SevenBit/Conf/LibraryConfig.hpp" +#include "SevenBit/Conf/EnvironmentVarsParserBuilder.hpp" #include "SevenBit/Conf/IConfigurationSource.hpp" -#include "SevenBit/Conf/ISettingParser.hpp" -#include "SevenBit/Conf/SettingParserBuilder.hpp" #include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" namespace sb::cf @@ -18,20 +17,20 @@ namespace sb::cf { private: std::string _prefix; - ISettingParser::Ptr _parser; + ISettingsParser::Ptr _parser; - EnvironmentVarsConfigurationSource(std::string prefix, ISettingParser::Ptr parser); + EnvironmentVarsConfigurationSource(std::string prefix, ISettingsParser::Ptr parser); public: using Ptr = std::unique_ptr; using SPtr = std::shared_ptr; [[nodiscard]] static SPtr create(std::string prefix, - ISettingParser::Ptr parser = SettingParserBuilder{}.build()); + ISettingsParser::Ptr parser = EnvironmentVarsParserBuilder{}.build()); [[nodiscard]] const std::string &getPrefix(); - [[nodiscard]] const ISettingParser &getSettingParser(); + [[nodiscard]] const ISettingsParser &getSettingParser(); IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; }; diff --git a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp index 00499a6..3d0a650 100644 --- a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp @@ -6,7 +6,6 @@ #include "SevenBit/Conf/Exceptions.hpp" #include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" - #ifdef _WIN32 extern "C" __declspec(dllimport) char **_environ; #define _7BIT_CONF_ENV_PTR _environ @@ -18,14 +17,14 @@ extern "C" char **environ; namespace sb::cf { INLINE EnvironmentVarsConfigurationSource::EnvironmentVarsConfigurationSource(std::string prefix, - ISettingParser::Ptr parser) + ISettingsParser::Ptr parser) : _prefix(std::move(prefix)), _parser(std::move(parser)) { details::utils::assertPtr(_parser); } INLINE EnvironmentVarsConfigurationSource::SPtr EnvironmentVarsConfigurationSource::create( - std::string prefix, ISettingParser::Ptr parser) + std::string prefix, ISettingsParser::Ptr parser) { return EnvironmentVarsConfigurationSource::SPtr( new EnvironmentVarsConfigurationSource{std::move(prefix), std::move(parser)}); @@ -33,7 +32,7 @@ namespace sb::cf INLINE const std::string &EnvironmentVarsConfigurationSource::getPrefix() { return _prefix; } - INLINE const ISettingParser &EnvironmentVarsConfigurationSource::getSettingParser() { return *_parser; } + INLINE const ISettingsParser &EnvironmentVarsConfigurationSource::getSettingParser() { return *_parser; } INLINE IConfigurationProvider::Ptr EnvironmentVarsConfigurationSource::build(IConfigurationBuilder &builder) { @@ -50,12 +49,7 @@ namespace sb::cf INLINE void EnvironmentVarsConfigurationProvider::load() { clear(); - auto &parser = _source->getSettingParser(); - for (auto &setting : getEnvVars()) - { - auto [keys, value] = parser.parse(setting); - update(keys, std::move(value)); - } + set(_source->getSettingParser().parse(getEnvVars())); } INLINE std::vector EnvironmentVarsConfigurationProvider::getEnvVars() diff --git a/README.md b/README.md index 1105b98..cc6c58b 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ auto configuration = ConfigurationBuilder{}.addCommandLine(configArgs).build(); ``` The command line configuration source can be more customized with the additional addCommandLine method -arguments: [SettingParserConfig](#setting-parser-config) or [SettingsParser](#custom-setting-parser). +arguments: [EnvironmentVarsParserConfig](#setting-parser-config) or [SettingsParser](#custom-setting-parser). #### Supported Types @@ -242,7 +242,7 @@ is '\_\_' (double underscore) and for '!' is '\_\_\_' (triple underscore). Setting Array:2!uint=123 would be rewritten as Array\_\_2\_\_\_uint=123 Same as command line source, environment variables configuration source can be more customized with -additional addEnvironmentVariables method arguments: [SettingParserConfig](#setting-parser-config) +additional addEnvironmentVariables method arguments: [EnvironmentVarsParserConfig](#setting-parser-config) or [SettingsParser](#custom-setting-parser). ### Json File @@ -406,11 +406,12 @@ int main(int argc, char **argv) ## Setting Parser Config -SettingParserConfig is a simple struct that contains the data used to configure the setting parser, by default it is +EnvironmentVarsParserConfig is a simple struct that contains the data used to configure the setting parser, by default +it is initialized with these values: ```cpp -struct SettingParserConfig +struct EnvironmentVarsParserConfig { std::vector settingPrefixes = {"--"}; std::vector settingSplitters = {"="}; @@ -448,7 +449,7 @@ using namespace sb::cf; int main(int argc, char **argv) { - SettingParserConfig envParserConfig; + EnvironmentVarsParserConfig envParserConfig; envParserConfig.keySplitters.clear(); envParserConfig.typeMarkers.clear(); @@ -464,7 +465,8 @@ int main(int argc, char **argv) } ``` -In this case, custom SettingParserConfig is used in addEnvironmentVariables method, keySplitters is cleared to prevent +In this case, custom EnvironmentVarsParserConfig is used in addEnvironmentVariables method, keySplitters is cleared to +prevent extracting nested keys and typeMarkers is cleared to prevent type extraction. ## Custom Setting Parser @@ -492,7 +494,7 @@ struct MyTypeDeserializer final : IDeserializer int main(int argc, char **argv) { - SettingParserConfig envParserConfig; + EnvironmentVarsParserConfig envParserConfig; envParserConfig.keySplitters.clear(); envParserConfig.settingPrefixes.emplace_back("//"); envParserConfig.defaultType = "myType"; diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 363eb25..aa92e55 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -10,23 +10,20 @@ FetchContent_MakeAvailable(googletest) include_directories(./Helpers) -if(_7BIT_CONF_BUILD_UNIT_TESTS) - add_subdirectory(Unit) -endif() +if (_7BIT_CONF_BUILD_UNIT_TESTS) + add_subdirectory(Unit) +endif () -if(_7BIT_CONF_BUILD_INTEGRATION_TESTS) - add_subdirectory(Integration) -endif() +if (_7BIT_CONF_BUILD_INTEGRATION_TESTS) + add_subdirectory(Integration) +endif () -if(_7BIT_CONF_BUILD_E2E_TESTS) - add_subdirectory(EndToEnd) -endif() +if (_7BIT_CONF_BUILD_E2E_TESTS) + add_subdirectory(EndToEnd) +endif () file(GLOB_RECURSE FILES CONFIGURE_DEPENDS ./Helpers/Files/*) file(GLOB_RECURSE FILES_DIR CONFIGURE_DEPENDS ./Helpers/Files/Directory/*) file(COPY ${FILES} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) file(COPY ${FILES_DIR} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Directory) - -file(COPY ${FILES} DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY ${FILES_DIR} DESTINATION ${CMAKE_BINARY_DIR}/Directory) \ No newline at end of file diff --git a/Tests/EndToEnd/CMakeLists.txt b/Tests/EndToEnd/CMakeLists.txt index 203048e..3beb8c3 100644 --- a/Tests/EndToEnd/CMakeLists.txt +++ b/Tests/EndToEnd/CMakeLists.txt @@ -1,7 +1,10 @@ +add_executable(CliEcho Cli/echo.cpp) -if(_7BIT_DI_BUILD_EXAMPLES) - find_package(Python REQUIRED) +target_link_libraries(CliEcho 7bitConf) - # add_test(NAME EndToEnd.Examples COMMAND ${Python_EXECUTABLE} test_examples.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Examples) -endif() +find_package(Python REQUIRED) + +add_test(NAME EndToEnd.CommandLine + COMMAND ${Python_EXECUTABLE} test.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + CONFIGURATIONS --exe $ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Cli) diff --git a/Tests/EndToEnd/Cli/echo.cpp b/Tests/EndToEnd/Cli/echo.cpp new file mode 100644 index 0000000..f097ecd --- /dev/null +++ b/Tests/EndToEnd/Cli/echo.cpp @@ -0,0 +1,13 @@ +#include +#include + +using namespace sb::cf; + +int main(int argc, char **argv) +{ + IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addCommandLine(argc, argv) + .build(); + std::cout << *configuration; + return 0; +} diff --git a/Tests/EndToEnd/Cli/test.py b/Tests/EndToEnd/Cli/test.py new file mode 100644 index 0000000..0512b76 --- /dev/null +++ b/Tests/EndToEnd/Cli/test.py @@ -0,0 +1,59 @@ +import json +import os +import subprocess +import sys + + +def getBinDir(): + return '/Users/sylwesterdawida/7bitConf/cmake-build-debug/bin' + if len(sys.argv) != 2: + raise Exception("binary directory not provided") + binDir = sys.argv[1] + if not os.path.exists(binDir): + raise Exception("Binary directory does not exist") + return binDir + + +class CliEchoTest: + def __init__(self, binDir): + self.binDir = binDir + self.echoExecPath = os.path.join(self.binDir, "CliEcho" + ('.exe' if sys.platform == 'win32' else '')) + self.testsData = self.__getTestsData() + + def __getTestsData(self): + with open('testData.json') as data: + return json.load(data) + + def __runTest(self, args, expectedJson): + result = subprocess.run([self.echoExecPath, *args], capture_output=True, text=True) + if result.returncode: + raise Exception(f"test returned non zero code {result.returncode}") + formatedOutput = json.dumps(json.loads(result.stdout)) + formatedExpected = json.dumps(expectedJson) + if formatedExpected != formatedOutput: + raise Exception(f"result of running test: '{formatedOutput}' does not match expected: '{formatedExpected}'") + + def __runTestAndSummarize(self, testData): + args, expectedJson = testData + try: + self.__runTest(args, expectedJson) + print(f"Test for args: {args} succeeded") + return True + except Exception as e: + print(f"Test for args: {args} failed: {e}") + return False + + def run(self): + allTests = len(self.testsData) + succeededTests = 0 + for count, testData in enumerate(self.testsData, start=1): + print(f"Test {count}/{allTests}") + succeededTests += self.__runTestAndSummarize(testData) + if succeededTests == allTests: + print(f"All test succeeded: {succeededTests}/{allTests}") + else: + raise Exception(f"Some tests failed: {allTests - succeededTests}/{allTests}") + + +if __name__ == "__main__": + CliEchoTest(getBinDir()).run() diff --git a/Tests/EndToEnd/Cli/testData.json b/Tests/EndToEnd/Cli/testData.json new file mode 100644 index 0000000..df6aedb --- /dev/null +++ b/Tests/EndToEnd/Cli/testData.json @@ -0,0 +1,39 @@ +[ + [ + [ + "testarg=123" + ], + { + "testarg": "123" + } + ], + [ + [ + "--testarg", + "123" + ], + { + "testarg": "123" + } + ], + [ + [ + "--testarg", + "123", + "/testarg2", + "12", + "testarg3=321", + "testarg4=2", + "testarg5", + "--testarg6" + ], + { + "testarg": "123", + "testarg2": "12", + "testarg3": "321", + "testarg4": "2", + "testarg5": "", + "testarg6": "" + } + ] +] diff --git a/Tests/EndToEnd/Examples/expectedTestData.json b/Tests/EndToEnd/Examples/expectedTestData.json deleted file mode 100644 index aa32ea6..0000000 --- a/Tests/EndToEnd/Examples/expectedTestData.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "Cli": "", - "CoreClasses": "", - "ExternalSingleton": "^Hello from service!", - "FactoryFunctions": "^Hello from service!", - "InjectingMultipleServices": "^work all: work A done! work B done! work C done! \nsingle work: work C done!", - "InjectingServiceProvider": "^actionA, actionB executed.", - "InjectingServices": "^actionA, actionB executed.", - "InjectionRules": "", - "SeparateImplementation": "^Hello from service.", - "ServicesLifeTime": "\nSingletons comparison\nrootProvider\\s+== rootProvider:\\s+1\nrootProvider\\s+== scopedProvider:\\s+1\nscopedProvider\\s+== scopedProvider:\\s+1\n\nScoped comparison\nrootProvider\\s+== rootProvider:\\s+1\nrootProvider\\s+== scopedProvider:\\s+0\nscopedProvider\\s+== scopedProvider:\\s+1\n\nTransient comparison\nrootProvider\\s+== rootProvider:\\s+0\nrootProvider\\s+== scopedProvider:\\s+0\nscopedProvider\\s+== scopedProvider:\\s+0\n", - "Simple": "^part a done!\npart b done!\n", - "ServiceAliases": "actionA from top service, actionB from top service executed." -} diff --git a/Tests/EndToEnd/Examples/test_examples.py b/Tests/EndToEnd/Examples/test_examples.py deleted file mode 100644 index 5e664dc..0000000 --- a/Tests/EndToEnd/Examples/test_examples.py +++ /dev/null @@ -1,69 +0,0 @@ -import sys -import os -import subprocess -import re -import json - - -def getBinDir(): - if len(sys.argv) != 2: - raise Exception("binary directory not provided") - binDir = sys.argv[1] - if not os.path.exists(binDir): - raise Exception("Binary directory does not exist") - return binDir - - -class ExamplesTest: - def __init__(self, binDir): - self.binDir = binDir - self.examplesPaths = self.__getAvailableExamples() - self.expectedTestMap = self.__getExpectedTestMap() - - def __getAvailableExamples(self): - paths = [os.path.join(self.binDir, f) for f in os.listdir(self.binDir) if - "Example" in f and os.path.splitext(f)[1] in [".exe", ""]] - return [f for f in paths if os.path.isfile(f)] - - def __getExpectedTestMap(self): - with open('expectedTestData.json') as data: - return json.load(data) - - def __runTest(self, examplePath, expected): - if expected is None: - raise Exception(f"example was not expected") - result = subprocess.run([examplePath], capture_output=True, text=True) - if result.returncode: - raise Exception(f"example returned non zero code {result.returncode}") - if not re.search(expected, result.stdout): - raise Exception(f"result of running example: '{result.stdout}' does not match expected: '{expected}'") - - def __runTestAndSummarize(self, examplePath): - fileName = os.path.basename(examplePath) - name = os.path.splitext(fileName)[0].replace("Example", "") - try: - self.__runTest(examplePath, self.expectedTestMap.get(name)) - print(f"{name} example test succeeded") - return True - except Exception as e: - print(f"{name} example test failed: {e}") - finally: - del self.expectedTestMap[name] - return False - - def run(self): - allTests = len(self.examplesPaths) - succeededTests = 0 - for count, examplePath in enumerate(self.examplesPaths, start=1): - print(f"Test {count}/{allTests}") - succeededTests += self.__runTestAndSummarize(examplePath) - if len(self.expectedTestMap): - raise Exception(f"Some expected tests were not run: {self.expectedTestMap.keys()}") - if succeededTests == allTests: - print(f"All test succeeded: {succeededTests}/{allTests}") - else: - raise Exception(f"Some tests failed: {allTests - succeededTests}/{allTests}") - - -if __name__ == "__main__": - ExamplesTest(getBinDir()).run() diff --git a/Tests/Unit/CMakeLists.txt b/Tests/Unit/CMakeLists.txt index 8386c79..dd9c363 100644 --- a/Tests/Unit/CMakeLists.txt +++ b/Tests/Unit/CMakeLists.txt @@ -1,13 +1,13 @@ file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp) add_executable(UnitTests - ${SOURCES} + ${SOURCES} ) target_link_libraries(UnitTests PUBLIC - GTest::gtest - GTest::gmock - 7bitConf + GTest::gtest + GTest::gmock + 7bitConf ) file(GLOB_RECURSE FILES CONFIGURE_DEPENDS Files/*) @@ -21,4 +21,4 @@ file(COPY ${FILES_DIR} DESTINATION ${CMAKE_BINARY_DIR}/Directory) include(GoogleTest) gtest_discover_tests(UnitTests - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) \ No newline at end of file + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/Tests/Unit/SettingParserBuilderTest.cpp b/Tests/Unit/SettingParserBuilderTest.cpp index 45e3f09..0ea32aa 100644 --- a/Tests/Unit/SettingParserBuilderTest.cpp +++ b/Tests/Unit/SettingParserBuilderTest.cpp @@ -4,10 +4,10 @@ // #include "Mocks/DeserializerMock.hpp" // #include "Mocks/SettingSplitterMock.hpp" // #include "Mocks/ValueDeserializersMapMock.hpp" -// #include "SevenBit/Conf/Details/SettingParser.hpp" +// #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" // #include "SevenBit/Conf/Details/SettingSplitter.hpp" // #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -// #include "SevenBit/Conf/SettingParserBuilder.hpp" +// #include "SevenBit/Conf/EnvironmentVarsParserBuilder.hpp" // // class SettingParserBuilderTest : public testing::Test // { @@ -42,7 +42,7 @@ // { // sb::cf::SettingParserBuilder builder; // -// sb::cf::SettingParserConfig config; +// sb::cf::EnvironmentVarsParserConfig config; // config.defaultType = "int"; // config.throwOnUnknownType = false; // config.allowEmptyKeys = true; diff --git a/Tests/Unit/SettingParserTest.cpp b/Tests/Unit/SettingParserTest.cpp index a4dedc6..61f6496 100644 --- a/Tests/Unit/SettingParserTest.cpp +++ b/Tests/Unit/SettingParserTest.cpp @@ -5,7 +5,7 @@ // #include "Mocks/DeserializerMock.hpp" // #include "Mocks/SettingSplitterMock.hpp" // #include "Mocks/ValueDeserializersMapMock.hpp" -// #include "SevenBit/Conf/Details/SettingParser.hpp" +// #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" // #include "SevenBit/Conf/Exceptions.hpp" // // class SettingParserTest : public testing::Test From 7ebac9ce41c9ac11cfe48eb9fa3b41107d503512 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Wed, 21 Feb 2024 19:41:34 +0100 Subject: [PATCH 06/31] refactor split parser builders --- .../Conf/CommandLineParserBuilder.hpp | 2 - .../SevenBit/Conf/CommandLineParserConfig.hpp | 4 +- .../SevenBit/Conf/Details/ContainerUtils.hpp | 38 +++++ .../Conf/Details/DefaultDeserializers.hpp | 2 +- .../Conf/Details/Impl/CommandLineParser.hpp | 13 +- .../Conf/Details/Impl/Configuration.hpp | 6 +- .../Conf/Details/Impl/Deserializers.hpp | 10 +- .../Details/Impl/EnvironmentVarsParser.hpp | 6 +- .../Conf/Details/Impl/JsonObjectExt.hpp | 31 ++-- .../Conf/Details/Impl/SettingSplitter.hpp | 8 +- .../Impl/{Utils.hpp => StringUtils.hpp} | 75 +++++----- .../Details/Impl/ValueDeserializersMap.hpp | 1 - Include/SevenBit/Conf/Details/JsonExt.hpp | 4 +- Include/SevenBit/Conf/Details/Require.hpp | 27 ++++ Include/SevenBit/Conf/Details/StringUtils.hpp | 110 ++++++++++++++ Include/SevenBit/Conf/Details/Utils.hpp | 135 ------------------ .../Conf/Details/ValueDeserializersMap.hpp | 4 +- .../Conf/EnvironmentVarsParserConfig.hpp | 2 +- .../SevenBit/Conf/IConfigurationBuilder.hpp | 15 +- .../Conf/Impl/CommandLineParserBuilder.hpp | 28 ++-- .../Conf/Impl/ConfigurationBuilder.hpp | 9 +- .../Impl/EnvironmentVarsParserBuilder.hpp | 4 +- .../Sources/Impl/ChainedConfiguration.hpp | 14 +- .../Sources/Impl/CommandLineConfiguration.hpp | 6 +- .../Impl/EnvironmentVarsConfiguration.hpp | 10 +- .../Sources/Impl/InMemoryConfiguration.hpp | 4 +- .../Conf/Sources/Impl/JsonConfiguration.hpp | 4 +- .../Sources/Impl/JsonFileConfiguration.hpp | 4 +- .../Sources/Impl/JsonStreamConfiguration.hpp | 4 +- .../Sources/Impl/KeyPerFileConfiguration.hpp | 58 +++++--- .../Conf/Sources/Impl/MapConfiguration.hpp | 18 +-- .../Conf/Sources/KeyPerFileConfiguration.hpp | 5 + .../Conf/Sources/MapConfiguration.hpp | 8 +- Tests/Unit/SettingSplitterTest.cpp | 2 +- Tests/Unit/UtilsTest.cpp | 43 +++--- 35 files changed, 383 insertions(+), 331 deletions(-) create mode 100644 Include/SevenBit/Conf/Details/ContainerUtils.hpp rename Include/SevenBit/Conf/Details/Impl/{Utils.hpp => StringUtils.hpp} (60%) create mode 100644 Include/SevenBit/Conf/Details/Require.hpp create mode 100644 Include/SevenBit/Conf/Details/StringUtils.hpp delete mode 100644 Include/SevenBit/Conf/Details/Utils.hpp diff --git a/Include/SevenBit/Conf/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/CommandLineParserBuilder.hpp index a190bdc..3fdfdd9 100644 --- a/Include/SevenBit/Conf/CommandLineParserBuilder.hpp +++ b/Include/SevenBit/Conf/CommandLineParserBuilder.hpp @@ -37,8 +37,6 @@ namespace sb::cf ISettingsParser::Ptr build(); private: - bool tryRemoveWhiteSpaceSplitters(); - ISettingSplitter::Ptr getSplitter(); IValueDeserializersMap::Ptr getValueDeserializersMap(); diff --git a/Include/SevenBit/Conf/CommandLineParserConfig.hpp b/Include/SevenBit/Conf/CommandLineParserConfig.hpp index 1038c26..c8cbbd4 100644 --- a/Include/SevenBit/Conf/CommandLineParserConfig.hpp +++ b/Include/SevenBit/Conf/CommandLineParserConfig.hpp @@ -9,8 +9,8 @@ namespace sb::cf { struct CommandLineParserConfig { - std::vector prefixes = {"--", "/"}; - std::vector splitters = {"=", " "}; + std::vector optionPrefixes = {"--", "/"}; + std::vector optionSplitters = {"=", " "}; std::vector keySplitters = {":"}; std::vector typeMarkers = {"!"}; std::string_view defaultType = "string"; diff --git a/Include/SevenBit/Conf/Details/ContainerUtils.hpp b/Include/SevenBit/Conf/Details/ContainerUtils.hpp new file mode 100644 index 0000000..f9a2ae9 --- /dev/null +++ b/Include/SevenBit/Conf/Details/ContainerUtils.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/Exceptions.hpp" + +namespace sb::cf::details +{ + struct ContainerUtils + { + template static TIt removeIf(TIt first, TIt last, TPred &&p) + { + first = std::find_if(first, last, p); + if (first != last) + { + for (auto i = first; ++i != last;) + { + if (!p(*i)) + { + *first++ = std::move(*i); + } + } + } + return first; + } + + template static size_t eraseIf(std::vector &vec, TPred &&p) + { + const auto it = removeIf(vec.begin(), vec.end(), std::forward(p)); + const size_t removedCnt = vec.end() - it; + vec.erase(it, vec.end()); + return removedCnt; + } + }; +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp index b1d95f6..bb25f55 100644 --- a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp +++ b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp @@ -6,7 +6,7 @@ namespace sb::cf { struct DefaultDeserializers { - static void addDefault(std::vector> &deserializers) + static void add(std::vector> &deserializers) { deserializers.emplace_back("string", std::make_unique()); deserializers.emplace_back("bool", std::make_unique()); diff --git a/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp b/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp index 159468f..549e9fc 100644 --- a/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp +++ b/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp @@ -3,7 +3,8 @@ #include #include "SevenBit/Conf/Details/CommandLineParser.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Exceptions.hpp" #include @@ -17,8 +18,8 @@ namespace sb::cf::details : _optionSplitter(std::move(optionSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)), _optionPrefixes(std::move(optionPrefixes)), _considerSeparated(considerSeparated) { - utils::assertPtr(_optionSplitter); - utils::assertPtr(_valueDeserializersMap); + Require::notNull(_optionSplitter); + Require::notNull(_valueDeserializersMap); } INLINE JsonObject CommandLineParser::parse(const std::vector &arguments) const @@ -44,11 +45,11 @@ namespace sb::cf::details const std::vector &arguments, size_t &index) const { auto argument = arguments[index]; - const auto hasOptionPrefix = tryRemoveOptionPrefix(argument); + const auto hadOptionPrefix = tryRemoveOptionPrefix(argument); auto [keys, type, value] = _optionSplitter->split(argument); - if (_considerSeparated && hasOptionPrefix && !value && index + 1 < arguments.size()) + if (_considerSeparated && hadOptionPrefix && !value && index + 1 < arguments.size()) { if (auto nextArgument = arguments[index + 1]; !tryGetOptionPrefix(nextArgument)) { @@ -73,7 +74,7 @@ namespace sb::cf::details { for (auto &optionPrefix : _optionPrefixes) { - if (utils::startsWith(argument, optionPrefix)) + if (StringUtils::startsWith(argument, optionPrefix)) { return optionPrefix; } diff --git a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp index 7b627b8..fed0edb 100644 --- a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp @@ -5,7 +5,7 @@ #include "SevenBit/Conf/Details/Configuration.hpp" #include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Exceptions.hpp" namespace sb::cf @@ -108,7 +108,7 @@ namespace sb::cf configRoot.clear(); for (auto &provider : _providers) { - details::utils::assertPtr(provider); + details::Require::notNull(provider); provider->load(); details::JsonExt::deepMerge(configRoot, std::move(provider->getConfiguration())); } @@ -120,7 +120,7 @@ namespace sb::cf INLINE JsonValue &Configuration::throwNotFoundException(const std::vector &key) const { - throw ValueNotFoundException{"Value was not found for key: " + details::utils::joinViews(key, ":")}; + throw ValueNotFoundException{"Value was not found for key: " + details::StringUtils::join(key, ":")}; } INLINE JsonValue &Configuration::throwNotFoundException(std::string_view key) const diff --git a/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp b/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp index 6596008..e9414f1 100644 --- a/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp @@ -4,7 +4,7 @@ #include #include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" namespace sb::cf::details { @@ -15,22 +15,22 @@ namespace sb::cf::details INLINE JsonValue BoolDeserializer::deserialize(std::optional value) const { - return value && details::utils::stringTo(*value); + return value && StringUtils::convertTo(*value); } INLINE JsonValue IntDeserializer::deserialize(std::optional value) const { - return value ? details::utils::stringTo(*value) : 0; + return value ? StringUtils::convertTo(*value) : 0; } INLINE JsonValue UIntDeserializer::deserialize(std::optional value) const { - return value ? details::utils::stringTo(*value) : 0; + return value ? StringUtils::convertTo(*value) : 0; } INLINE JsonValue DoubleDeserializer::deserialize(std::optional value) const { - return value ? details::utils::stringTo(*value) : 0.0; + return value ? StringUtils::convertTo(*value) : 0.0; } INLINE JsonValue NullDeserializer::deserialize(std::optional value) const { return json::null; } diff --git a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp index 3c3b1f0..ac86380 100644 --- a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp +++ b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp @@ -3,7 +3,7 @@ #include #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Exceptions.hpp" namespace sb::cf::details @@ -12,8 +12,8 @@ namespace sb::cf::details IValueDeserializersMap::Ptr valueDeserializersMap) : _settingSplitter(std::move(settingSplitter)), _valueDeserializersMap(std::move(valueDeserializersMap)) { - utils::assertPtr(_settingSplitter); - utils::assertPtr(_valueDeserializersMap); + Require::notNull(_settingSplitter); + Require::notNull(_valueDeserializersMap); } INLINE JsonObject EnvironmentVarsParser::parse(const std::vector &envVariables) const diff --git a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp b/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp index c289b65..0482585 100644 --- a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp +++ b/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp @@ -3,7 +3,7 @@ #include #include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Exceptions.hpp" #include "SevenBit/Conf/Json.hpp" @@ -59,13 +59,13 @@ namespace sb::cf::details INLINE JsonValue *JsonExt::find(JsonArray &json, std::string_view key) { - auto [success, index] = details::utils::tryStringTo(key); + auto [success, index] = StringUtils::tryConvertTo(key); return success ? find(json, index) : nullptr; } INLINE const JsonValue *JsonExt::find(const JsonArray &json, std::string_view key) { - auto [success, index] = details::utils::tryStringTo(key); + auto [success, index] = StringUtils::tryConvertTo(key); return success ? find(json, index) : nullptr; } @@ -82,12 +82,12 @@ namespace sb::cf::details INLINE JsonValue *JsonExt::deepFind(JsonValue &json, std::string_view key) { - return deepFind(json, details::utils::split(key, ":")); + return deepFind(json, StringUtils::split(key, ":")); } INLINE const JsonValue *JsonExt::deepFind(const JsonValue &json, std::string_view key) { - return deepFind(json, details::utils::split(key, ":")); + return deepFind(json, StringUtils::split(key, ":")); } INLINE JsonValue *JsonExt::deepFind(JsonValue &json, const std::vector &key) @@ -102,12 +102,12 @@ namespace sb::cf::details INLINE JsonValue *JsonExt::deepFind(JsonObject &json, std::string_view key) { - return deepFind(json, details::utils::split(key, ":")); + return deepFind(json, StringUtils::split(key, ":")); } INLINE const JsonValue *JsonExt::deepFind(const JsonObject &json, std::string_view key) { - return deepFind(json, details::utils::split(key, ":")); + return deepFind(json, StringUtils::split(key, ":")); } INLINE JsonValue *JsonExt::deepFind(JsonObject &json, const std::vector &key) @@ -122,12 +122,12 @@ namespace sb::cf::details INLINE JsonValue *JsonExt::deepFind(JsonArray &json, std::string_view key) { - return deepFind(json, details::utils::split(key, ":")); + return deepFind(json, StringUtils::split(key, ":")); } INLINE const JsonValue *JsonExt::deepFind(const JsonArray &json, std::string_view key) { - return deepFind(json, details::utils::split(key, ":")); + return deepFind(json, StringUtils::split(key, ":")); } INLINE JsonValue *JsonExt::deepFind(JsonArray &json, const std::vector &key) @@ -142,7 +142,7 @@ namespace sb::cf::details INLINE JsonValue &JsonExt::getOrOverride(JsonValue &json, std::string_view key) { - auto isNumber = utils::isNumberString(key); + auto isNumber = StringUtils::isNumber(key); if (!json.is_object() && !json.is_array()) { json = isNumber ? JsonValue(JsonArray{}) : JsonValue(JsonObject{}); @@ -151,7 +151,7 @@ namespace sb::cf::details { if (isNumber) { - return getOrOverride(json.get_array(), utils::stringTo(key)); + return getOrOverride(json.get_array(), StringUtils::convertTo(key)); } json.set_object({}); } @@ -180,7 +180,7 @@ namespace sb::cf::details INLINE JsonValue &JsonExt::deepGetOrOverride(JsonObject &json, std::string_view key) { - return deepGetOrOverride(json, details::utils::split(key, ":")); + return deepGetOrOverride(json, StringUtils::split(key, ":")); } INLINE JsonValue &JsonExt::deepGetOrOverride(JsonObject &json, const std::vector &keys) @@ -244,7 +244,12 @@ namespace sb::cf::details } } - INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonValue value) + INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value) + { + deepGetOrOverride(json, keys) = std::move(value); + } + + INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonObject &&value) { deepGetOrOverride(json, keys) = std::move(value); } diff --git a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp index 6e397ee..d92f134 100644 --- a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp @@ -1,7 +1,7 @@ #pragma once #include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" namespace sb::cf::details { @@ -24,7 +24,7 @@ namespace sb::cf::details INLINE std::pair> SettingSplitter::splitSetting( const std::string_view setting) const { - if (auto breakResult = details::utils::tryBreak(setting, _settingSplitters)) + if (auto breakResult = StringUtils::tryBreak(setting, _settingSplitters)) { return {breakResult->first, breakResult->second}; } @@ -33,7 +33,7 @@ namespace sb::cf::details INLINE std::optional SettingSplitter::tryExtractType(std::string_view &key) const { - if (auto breakResult = details::utils::tryBreakFromEnd(key, _typeMarkers)) + if (auto breakResult = StringUtils::tryBreakFromEnd(key, _typeMarkers)) { key = breakResult->first; return breakResult->second; @@ -43,7 +43,7 @@ namespace sb::cf::details INLINE std::vector SettingSplitter::splitKey(const std::string_view key) const { - auto keys = details::utils::split(key, _keySplitters); + auto keys = StringUtils::split(key, _keySplitters); checkKeys(keys); return keys; } diff --git a/Include/SevenBit/Conf/Details/Impl/Utils.hpp b/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp similarity index 60% rename from Include/SevenBit/Conf/Details/Impl/Utils.hpp rename to Include/SevenBit/Conf/Details/Impl/StringUtils.hpp index cd6782b..3495362 100644 --- a/Include/SevenBit/Conf/Details/Impl/Utils.hpp +++ b/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp @@ -3,31 +3,30 @@ #include #include -#include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" -namespace sb::cf::details::utils +namespace sb::cf::details { - INLINE bool isNumberString(std::string_view str) + INLINE bool StringUtils::isNumber(std::string_view str) { return !str.empty() && std::all_of(str.begin(), str.end(), [](unsigned char ch) { return std::isdigit(ch); }); } - INLINE bool ignoreCaseLess(std::string_view str, std::string_view search) + INLINE bool StringUtils::ignoreCaseLess(std::string_view str, std::string_view search) { return std::lexicographical_compare( str.begin(), str.end(), search.begin(), search.end(), [](unsigned char cha, unsigned char chb) { return std::tolower(cha) < std::tolower(chb); }); } - INLINE bool ignoreCaseEqual(std::string_view str, std::string_view search) + INLINE bool StringUtils::ignoreCaseEqual(std::string_view str, std::string_view search) { return str.size() == search.size() && std::equal(str.begin(), str.end(), search.begin(), search.end(), [](unsigned char cha, unsigned char chb) { return std::tolower(cha) == std::tolower(chb); }); } - INLINE bool containsAt(std::string_view str, size_t index, std::string_view search) + INLINE bool StringUtils::containsAt(std::string_view str, size_t index, std::string_view search) { if (index >= str.size() || search.empty()) { @@ -36,8 +35,8 @@ namespace sb::cf::details::utils return str.compare(index, search.size(), search) == 0; } - INLINE std::optional containsAt(std::string_view str, size_t index, - const std::vector &searches) + INLINE std::optional StringUtils::containsAt(std::string_view str, size_t index, + const std::vector &searches) { for (auto &search : searches) { @@ -49,7 +48,7 @@ namespace sb::cf::details::utils return std::nullopt; } - INLINE bool containsAtFromEnd(std::string_view str, size_t index, std::string_view search) + INLINE bool StringUtils::containsAtFromEnd(std::string_view str, size_t index, std::string_view search) { if (index + 1 < search.size()) { @@ -58,8 +57,8 @@ namespace sb::cf::details::utils return containsAt(str, index + 1 - search.size(), search); } - INLINE std::optional containsAtFromEnd(std::string_view str, size_t index, - const std::vector &searches) + INLINE std::optional StringUtils::containsAtFromEnd(std::string_view str, size_t index, + const std::vector &searches) { for (auto &search : searches) { @@ -71,7 +70,7 @@ namespace sb::cf::details::utils return std::nullopt; } - INLINE bool startsWith(std::string_view str, std::string_view search) + INLINE bool StringUtils::startsWith(std::string_view str, std::string_view search) { auto searchIt = search.begin(); for (auto it = str.begin(); it != str.end() && searchIt != search.end(); ++it, ++searchIt) @@ -84,11 +83,19 @@ namespace sb::cf::details::utils return searchIt == search.end(); } - INLINE std::vector split(std::string_view str, std::string_view divider) + INLINE size_t StringUtils::startsWithWhiteSpace(std::string_view str) + { + const auto it = std::find_if(str.begin(), str.end(), [](const unsigned char ch) { return !std::isspace(ch); }); + return it - str.begin(); + } + + INLINE bool StringUtils::isWhiteSpace(std::string_view str) { return startsWithWhiteSpace(str) == str.size(); } + + INLINE std::vector StringUtils::split(std::string_view str, std::string_view separator) { std::vector result; std::string::size_type begin = 0, pos = 0; - for (; std::string_view::npos != (pos = str.find_first_of(divider, pos)); begin = (pos += divider.size())) + for (; std::string_view::npos != (pos = str.find_first_of(separator, pos)); begin = (pos += separator.size())) { result.push_back(str.substr(begin, pos - begin)); } @@ -96,12 +103,13 @@ namespace sb::cf::details::utils return result; } - INLINE std::vector split(std::string_view str, const std::vector ÷rs) + INLINE std::vector StringUtils::split(std::string_view str, + const std::vector &separators) { std::vector result; for (int i = 0; i < str.size(); ++i) { - if (auto foundDelim = containsAt(str, i, dividers)) + if (auto foundDelim = containsAt(str, i, separators)) { result.emplace_back(str.substr(0, i)); str.remove_prefix(foundDelim->size() + result.back().size()); @@ -112,12 +120,12 @@ namespace sb::cf::details::utils return result; } - INLINE std::optional> tryBreak( - std::string_view str, const std::vector ÷rs) + INLINE std::optional> StringUtils::tryBreak( + std::string_view str, const std::vector &separators) { for (size_t i = 0; i < str.size(); ++i) { - if (auto foundDelim = containsAt(str, i, dividers)) + if (auto foundDelim = containsAt(str, i, separators)) { return std::make_pair(str.substr(0, i), str.substr(i + foundDelim->size())); } @@ -125,12 +133,12 @@ namespace sb::cf::details::utils return std::nullopt; } - INLINE std::optional> tryBreakFromEnd( - std::string_view str, const std::vector ÷rs) + INLINE std::optional> StringUtils::tryBreakFromEnd( + std::string_view str, const std::vector &separators) { for (int i = static_cast(str.size()) - 1; i >= 0; --i) { - if (auto foundDelim = containsAtFromEnd(str, i, dividers)) + if (auto foundDelim = containsAtFromEnd(str, i, separators)) { return std::make_pair(str.substr(0, i + 1 - foundDelim->size()), str.substr(i + 1)); } @@ -138,26 +146,17 @@ namespace sb::cf::details::utils return std::nullopt; } - INLINE std::string joinViews(const std::vector &strings, const std::string ÷r) + INLINE std::string StringUtils::join(const std::vector &strs, const std::string &separator) { std::string res; - if (!strings.empty()) + if (!strs.empty()) { - for (size_t i = 0; i < strings.size() - 1; ++i) + for (size_t i = 0; i < strs.size() - 1; ++i) { - res += std::string{strings[i]} + divider; + res += std::string{strs[i]} + separator; } - res += strings.back(); + res += strs.back(); } return res; } - - INLINE inline size_t startsWhiteSpace(std::string_view str) - { - const auto it = std::find_if(str.begin(), str.end(), [](const unsigned char ch) { return !std::isspace(ch); }); - return it - str.begin(); - } - - INLINE inline bool isWhiteSpace(std::string_view str) { return startsWhiteSpace(str) == str.size(); } - -} // namespace sb::cf::details::utils +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp index d0ea77d..cd5e210 100644 --- a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp @@ -1,6 +1,5 @@ #pragma once -#include "SevenBit/Conf/Details/Utils.hpp" #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" #include "SevenBit/Conf/Exceptions.hpp" diff --git a/Include/SevenBit/Conf/Details/JsonExt.hpp b/Include/SevenBit/Conf/Details/JsonExt.hpp index 6b317d2..6c9d8c5 100644 --- a/Include/SevenBit/Conf/Details/JsonExt.hpp +++ b/Include/SevenBit/Conf/Details/JsonExt.hpp @@ -58,7 +58,9 @@ namespace sb::cf::details static void deepMerge(JsonObject &json, JsonObject override); - static void updateWith(JsonObject &json, const std::vector &keys, JsonValue value); + static void updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value); + + static void updateWith(JsonObject &json, const std::vector &keys, JsonObject &&value); static void checkSegmentSize(const std::vector &key); }; diff --git a/Include/SevenBit/Conf/Details/Require.hpp b/Include/SevenBit/Conf/Details/Require.hpp new file mode 100644 index 0000000..68e8b29 --- /dev/null +++ b/Include/SevenBit/Conf/Details/Require.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/Exceptions.hpp" + +namespace sb::cf::details +{ + struct Require + { + template static void notNull(const T *ptr) + { + if (!ptr) + { + throw NullPointerException(std::string{"Pointer of type: '"} + typeid(T).name() + "' cannot be null"); + } + } + + template static void notNull(const std::unique_ptr &ptr) { notNull(ptr.get()); } + + template static void notNull(const std::shared_ptr &ptr) { notNull(ptr.get()); } + }; +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/StringUtils.hpp b/Include/SevenBit/Conf/Details/StringUtils.hpp new file mode 100644 index 0000000..df55a8d --- /dev/null +++ b/Include/SevenBit/Conf/Details/StringUtils.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "SevenBit/Conf/LibraryConfig.hpp" + +#include "SevenBit/Conf/Exceptions.hpp" + +namespace sb::cf::details +{ + struct EXPORT StringUtils + { + static bool isNumber(std::string_view str); + + static bool ignoreCaseLess(std::string_view str, std::string_view search); + + static bool ignoreCaseEqual(std::string_view str, std::string_view search); + + static bool containsAt(std::string_view str, size_t index, std::string_view search); + + static std::optional containsAt(std::string_view str, size_t index, + const std::vector &searches); + + static bool containsAtFromEnd(std::string_view str, size_t index, std::string_view search); + + static std::optional containsAtFromEnd(std::string_view str, size_t index, + const std::vector &searches); + + static bool startsWith(std::string_view str, std::string_view search); + + static size_t startsWithWhiteSpace(std::string_view str); + + static bool isWhiteSpace(std::string_view str); + + static std::vector split(std::string_view str, std::string_view separator); + + static std::vector split(std::string_view str, + const std::vector &separators); + + static std::optional> tryBreak( + std::string_view str, const std::vector &separators); + + static std::optional> tryBreakFromEnd( + std::string_view str, const std::vector &separators); + + static std::string join(const std::vector &strs, const std::string &separator); + + template + static std::pair tryConvertTo(std::string_view str, const bool full = true) + { + TNumber number = 0; + str.remove_prefix(startsWithWhiteSpace(str)); + auto last = str.data() + str.size(); + auto res = std::from_chars(str.data(), last, number); + auto success = res.ec == std::errc{} && (!full || res.ptr == last); + return {success, number}; + } + + template static TNumber convertTo(std::string_view str, const bool full = true) + { + if (auto [success, number] = tryConvertTo(str, full); success) + { + return number; + } + throw ConfigException{"Cannot convert string to number: " + std::string{str}}; + } + }; + + template <> inline std::pair StringUtils::tryConvertTo(std::string_view str, const bool full) + { + try + { + std::string string{str}; + size_t processed = 0; + auto number = std::stod(string, &processed); + auto success = !full || processed == string.size(); + return {success, number}; + } + catch (std::exception &e) + { + return {false, 0.0}; + } + } + + template <> inline std::pair StringUtils::tryConvertTo(std::string_view str, const bool full) + { + if (ignoreCaseEqual(str, "true")) + { + return {true, true}; + } + else if (ignoreCaseEqual(str, "false")) + { + return {true, false}; + } + else if (auto [success, number] = tryConvertTo(str, full); success) + { + return {true, number}; + } + return {false, false}; + } +} // namespace sb::cf::details + +#ifdef _7BIT_CONF_ADD_IMPL +#include "SevenBit/Conf/Details/Impl/StringUtils.hpp" +#endif diff --git a/Include/SevenBit/Conf/Details/Utils.hpp b/Include/SevenBit/Conf/Details/Utils.hpp deleted file mode 100644 index 45c945a..0000000 --- a/Include/SevenBit/Conf/Details/Utils.hpp +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "SevenBit/Conf/LibraryConfig.hpp" - -#include "SevenBit/Conf/Exceptions.hpp" - -namespace sb::cf::details::utils -{ - EXPORT bool isNumberString(std::string_view str); - - EXPORT bool ignoreCaseLess(std::string_view str, std::string_view search); - - EXPORT bool ignoreCaseEqual(std::string_view str, std::string_view search); - - EXPORT bool containsAt(std::string_view str, size_t index, std::string_view search); - - EXPORT std::optional containsAt(std::string_view str, size_t index, - const std::vector &searches); - - EXPORT bool containsAtFromEnd(std::string_view str, size_t index, std::string_view search); - - EXPORT std::optional containsAtFromEnd(std::string_view str, size_t index, - const std::vector &searches); - - EXPORT bool startsWith(std::string_view str, std::string_view search); - - EXPORT std::vector split(std::string_view str, std::string_view divider); - - EXPORT std::vector split(std::string_view str, const std::vector ÷rs); - - EXPORT std::optional> tryBreak( - std::string_view str, const std::vector ÷rs); - - EXPORT std::optional> tryBreakFromEnd( - std::string_view str, const std::vector ÷rs); - - EXPORT std::string joinViews(const std::vector &strings, const std::string ÷r); - - template void assertPtr(const T *ptr) - { - if (!ptr) - { - throw NullPointerException(std::string{"Pointer of type: '"} + typeid(T).name() + "' cannot be null"); - } - } - - template void assertPtr(const std::unique_ptr &ptr) { assertPtr(ptr.get()); } - - template void assertPtr(const std::shared_ptr &ptr) { assertPtr(ptr.get()); } - - EXPORT inline size_t startsWhiteSpace(std::string_view str); - - EXPORT inline bool isWhiteSpace(std::string_view str); - - template std::pair tryStringTo(std::string_view str, bool full = true) - { - TNumber number = 0; - str.remove_prefix(startsWhiteSpace(str)); - auto last = str.data() + str.size(); - auto res = std::from_chars(str.data(), last, number); - auto success = res.ec == std::errc{} && (!full || res.ptr == last); - return {success, number}; - } - - template <> inline std::pair tryStringTo(std::string_view str, bool full) - { - try - { - std::string string{str}; - size_t processed = 0; - auto number = std::stod(string, &processed); - auto success = !full || processed == string.size(); - return {success, number}; - } - catch (std::exception &e) - { - return {false, 0.0}; - } - } - - template <> inline std::pair tryStringTo(std::string_view str, bool full) - { - if (ignoreCaseEqual(str, "true")) - { - return {true, true}; - } - else if (ignoreCaseEqual(str, "false")) - { - return {true, false}; - } - else if (auto [success, number] = tryStringTo(str, full); success) - { - return {true, number}; - } - return {false, false}; - } - - template TNumber stringTo(std::string_view str, bool full = true) - { - if (auto [success, number] = tryStringTo(str, full); success) - { - return number; - } - throw ConfigException{"Cannot convert string to number: " + std::string{str}}; - } - - template - static ForwardIt removeIf(ForwardIt first, ForwardIt last, UnaryPredicate &&p) - { - first = std::find_if(first, last, p); - if (first != last) - { - for (ForwardIt i = first; ++i != last;) - { - if (!p(*i)) - { - *first++ = std::move(*i); - } - } - } - return first; - } - -} // namespace sb::cf::details::utils - -#ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/Utils.hpp" -#endif diff --git a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp index d608227..2766ec5 100644 --- a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp @@ -7,7 +7,7 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/IValueDeserializersMap.hpp" namespace sb::cf::details @@ -21,7 +21,7 @@ namespace sb::cf::details template bool operator()(const T1 &s1, const T2 &s2) const { - return utils::ignoreCaseLess(s1, s2); + return StringUtils::ignoreCaseLess(s1, s2); } }; diff --git a/Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp b/Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp index ab5fe70..f91267d 100644 --- a/Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp +++ b/Include/SevenBit/Conf/EnvironmentVarsParserConfig.hpp @@ -9,7 +9,7 @@ namespace sb::cf { struct EnvironmentVarsParserConfig { - std::vector splitters = {"="}; + std::vector variableSplitters = {"="}; std::vector keySplitters = {":", "__"}; std::vector typeMarkers = {"!", "___"}; std::string_view defaultType = "string"; diff --git a/Include/SevenBit/Conf/IConfigurationBuilder.hpp b/Include/SevenBit/Conf/IConfigurationBuilder.hpp index 2996d36..5527ba8 100644 --- a/Include/SevenBit/Conf/IConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/IConfigurationBuilder.hpp @@ -44,7 +44,7 @@ namespace sb::cf virtual void clear() = 0; - template IConfigurationBuilder &addFrom(TFactory factory) { return add(factory(*this)); } + template IConfigurationBuilder &addFrom(TFactory &&factory) { return add(factory(*this)); } IConfigurationBuilder &addJsonFile(std::filesystem::path filePath) { @@ -68,10 +68,7 @@ namespace sb::cf return add(AppSettingsConfigurationSource::create(std::move(environmentName))); } - IConfigurationBuilder &addEnvironmentVariables() - { - return addEnvironmentVariables("", EnvironmentVarsParserConfig{}); - } + IConfigurationBuilder &addEnvironmentVariables() { return addEnvironmentVariables(""); } IConfigurationBuilder &addEnvironmentVariables(std::string prefix) { @@ -85,7 +82,7 @@ namespace sb::cf } template - IConfigurationBuilder &addEnvironmentVariables(std::string prefix, TBuilderFunc parserBuilderFunctor) + IConfigurationBuilder &addEnvironmentVariables(std::string prefix, TBuilderFunc &&parserBuilderFunctor) { EnvironmentVarsParserBuilder builder; parserBuilderFunctor(builder); @@ -118,7 +115,7 @@ namespace sb::cf } template - IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, TBuilderFunc parserBuilderFunctor) + IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, TBuilderFunc &&parserBuilderFunctor) { CommandLineParserBuilder builder; parserBuilderFunctor(builder); @@ -126,7 +123,7 @@ namespace sb::cf } template - IConfigurationBuilder &addCommandLine(std::vector args, TBuilderFunc parserBuilderFunctor) + IConfigurationBuilder &addCommandLine(std::vector args, TBuilderFunc &&parserBuilderFunctor) { CommandLineParserBuilder builder; parserBuilderFunctor(builder); @@ -150,7 +147,7 @@ namespace sb::cf IConfigurationBuilder &addKeyPerFile(std::filesystem::path directoryPath) { - return addKeyPerFile(std::move(directoryPath), false, ""); + return addKeyPerFile(std::move(directoryPath), false); } IConfigurationBuilder &addKeyPerFile(std::filesystem::path directoryPath, bool isOptional) diff --git a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp index 5107f00..ff106d7 100644 --- a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp @@ -1,11 +1,14 @@ #pragma once +#include + #include "SevenBit/Conf/CommandLineParserBuilder.hpp" #include "SevenBit/Conf/Details/CommandLineParser.hpp" +#include "SevenBit/Conf/Details/ContainerUtils.hpp" #include "SevenBit/Conf/Details/DefaultDeserializers.hpp" #include "SevenBit/Conf/Details/Deserializers.hpp" #include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" namespace sb::cf @@ -38,25 +41,18 @@ namespace sb::cf INLINE CommandLineParserBuilder &CommandLineParserBuilder::useDefaultValueDeserializers() { - DefaultDeserializers::addDefault(_valueDeserializers); + DefaultDeserializers::add(_valueDeserializers); return *this; } INLINE ISettingsParser::Ptr CommandLineParserBuilder::build() { - auto hadWhiteSpaceSplitters = tryRemoveWhiteSpaceSplitters(); - return std::make_unique( - getSplitter(), getValueDeserializersMap(), std::move(getConfig().prefixes), hadWhiteSpaceSplitters); - } + auto &config = getConfig(); + auto hadWhiteSpaceSplitters = details::ContainerUtils::eraseIf( + config.optionSplitters, [](const auto splitter) { return details::StringUtils::isWhiteSpace(splitter); }); - INLINE bool CommandLineParserBuilder::tryRemoveWhiteSpaceSplitters() - { - auto &splitters = getConfig().splitters; - const auto it = details::utils::removeIf(splitters.begin(), splitters.end(), - [](auto splitter) { return details::utils::isWhiteSpace(splitter); }); - const auto removeCnt = splitters.end() - it; - splitters.erase(it, splitters.end()); - return removeCnt; + return std::make_unique(getSplitter(), getValueDeserializersMap(), + std::move(config.optionPrefixes), hadWhiteSpaceSplitters); } INLINE ISettingSplitter::Ptr CommandLineParserBuilder::getSplitter() @@ -65,8 +61,8 @@ namespace sb::cf { auto &config = getConfig(); useSplitter(std::make_unique( - std::move(config.splitters), std::move(config.typeMarkers), - std::move(config.keySplitters), config.allowEmptyKeys)); + std::move(config.optionSplitters), std::move(config.typeMarkers), std::move(config.keySplitters), + config.allowEmptyKeys)); } return std::move(_splitter); } diff --git a/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp b/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp index 44b7b97..cab7e7b 100644 --- a/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp @@ -2,8 +2,7 @@ #include "SevenBit/Conf/ConfigurationBuilder.hpp" #include "SevenBit/Conf/Details/Configuration.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" - +#include "SevenBit/Conf/Details/Require.hpp" namespace sb::cf { @@ -12,13 +11,13 @@ namespace sb::cf { for (auto &source : _sources) { - details::utils::assertPtr(source); + details::Require::notNull(source); } } INLINE IConfigurationBuilder &ConfigurationBuilder::add(IConfigurationSource::SPtr source) { - details::utils::assertPtr(source); + details::Require::notNull(source); _sources.push_back(std::move(source)); return *this; } @@ -29,7 +28,7 @@ namespace sb::cf providers.reserve(_sources.size()); for (auto &source : _sources) { - details::utils::assertPtr(source); + details::Require::notNull(source); providers.emplace_back(source->build(*this)); } return std::make_unique(std::move(providers)); diff --git a/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp b/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp index 3836eba..956388f 100644 --- a/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp @@ -36,7 +36,7 @@ namespace sb::cf INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useDefaultValueDeserializers() { - DefaultDeserializers::addDefault(_valueDeserializers); + DefaultDeserializers::add(_valueDeserializers); return *this; } @@ -51,7 +51,7 @@ namespace sb::cf { auto &config = getConfig(); useSplitter( - std::make_unique(std::move(config.splitters), std::move(config.typeMarkers), + std::make_unique(std::move(config.variableSplitters), std::move(config.typeMarkers), std::move(config.keySplitters), config.allowEmptyKeys)); } return std::move(_splitter); diff --git a/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp index 337dfe8..d16f46d 100644 --- a/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp @@ -2,11 +2,9 @@ #include -#include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Sources/ChainedConfiguration.hpp" - namespace sb::cf { INLINE ChainedConfigurationSource::ChainedConfigurationSource(std::vector sources) @@ -14,7 +12,7 @@ namespace sb::cf { for (auto &source : _sources) { - details::utils::assertPtr(source); + details::Require::notNull(source); } } @@ -26,7 +24,7 @@ namespace sb::cf INLINE void ChainedConfigurationSource::add(IConfigurationSource::SPtr source) { - details::utils::assertPtr(source); + details::Require::notNull(source); _sources.push_back(std::move(source)); } @@ -36,7 +34,7 @@ namespace sb::cf providers.reserve(_sources.size()); for (auto &source : _sources) { - details::utils::assertPtr(source); + details::Require::notNull(source); providers.emplace_back(source->build(builder)); } return std::make_unique(std::move(providers)); @@ -48,7 +46,7 @@ namespace sb::cf { for (auto &provider : _providers) { - details::utils::assertPtr(provider); + details::Require::notNull(provider); } } @@ -57,7 +55,7 @@ namespace sb::cf clear(); for (auto &provider : _providers) { - details::utils::assertPtr(provider); + details::Require::notNull(provider); provider->load(); update(std::move(provider->getConfiguration())); } diff --git a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp index c3fcfaf..1cfdc11 100644 --- a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp @@ -1,6 +1,6 @@ #pragma once -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" namespace sb::cf @@ -9,7 +9,7 @@ namespace sb::cf ISettingsParser::Ptr parser) : _args(std::move(args)), _parser(std::move(parser)) { - details::utils::assertPtr(_parser); + details::Require::notNull(_parser); } INLINE CommandLineConfigurationSource::SPtr CommandLineConfigurationSource::create(int argc, @@ -48,7 +48,7 @@ namespace sb::cf CommandLineConfigurationSource::SPtr source) : _source(std::move(source)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE void CommandLineConfigurationProvider::load() diff --git a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp index 3d0a650..3b0a45e 100644 --- a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp @@ -2,8 +2,8 @@ #include -#include "SevenBit/Conf/Details/Utils.hpp" -#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Details/Require.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" #ifdef _WIN32 @@ -20,7 +20,7 @@ namespace sb::cf ISettingsParser::Ptr parser) : _prefix(std::move(prefix)), _parser(std::move(parser)) { - details::utils::assertPtr(_parser); + details::Require::notNull(_parser); } INLINE EnvironmentVarsConfigurationSource::SPtr EnvironmentVarsConfigurationSource::create( @@ -43,7 +43,7 @@ namespace sb::cf EnvironmentVarsConfigurationSource::SPtr source) : _source(std::move(source)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE void EnvironmentVarsConfigurationProvider::load() @@ -63,7 +63,7 @@ namespace sb::cf { result.push_back(envStr); } - else if (details::utils::startsWith(envStr, prefix)) + else if (details::StringUtils::startsWith(envStr, prefix)) { result.push_back(envStr.substr(prefix.size())); } diff --git a/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp index 53e9ace..962ef12 100644 --- a/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp @@ -1,7 +1,7 @@ #pragma once #include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Sources/InMemoryConfiguration.hpp" namespace sb::cf @@ -26,7 +26,7 @@ namespace sb::cf INLINE InMemoryConfigurationProvider::InMemoryConfigurationProvider(InMemoryConfigurationSource::SPtr source) : _source(std::move(source)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE void InMemoryConfigurationProvider::load() diff --git a/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp index ac83e84..f59bb94 100644 --- a/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp @@ -1,6 +1,6 @@ #pragma once -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Sources/JsonConfiguration.hpp" namespace sb::cf @@ -25,7 +25,7 @@ namespace sb::cf INLINE JsonConfigurationProvider::JsonConfigurationProvider(JsonConfigurationSource::SPtr source) : _source(std::move(source)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE void JsonConfigurationProvider::load() { set(_source->getJson()); } diff --git a/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp index b940ff6..81b7354 100644 --- a/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp @@ -3,7 +3,7 @@ #include #include -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Exceptions.hpp" #include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" @@ -32,7 +32,7 @@ namespace sb::cf INLINE JsonFileConfigurationProvider::JsonFileConfigurationProvider(JsonFileConfigurationSource::SPtr source) : _source(std::move(source)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE void JsonFileConfigurationProvider::load() { set(getJsonFromFile()); } diff --git a/Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp index e80dc70..918edd7 100644 --- a/Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonStreamConfiguration.hpp @@ -2,7 +2,7 @@ #include -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Exceptions.hpp" #include "SevenBit/Conf/Sources/JsonStreamConfiguration.hpp" @@ -25,7 +25,7 @@ namespace sb::cf INLINE JsonStreamConfigurationProvider::JsonStreamConfigurationProvider(JsonStreamConfigurationSource::SPtr source) : _source(std::move(source)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE void JsonStreamConfigurationProvider::load() { set(getJsonFromStream()); } diff --git a/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp index 84ae8b0..f66bc9a 100644 --- a/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp @@ -1,7 +1,7 @@ #pragma once #include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Sources/ChainedConfiguration.hpp" #include "SevenBit/Conf/Sources/JsonFileConfiguration.hpp" #include "SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp" @@ -11,15 +11,15 @@ namespace sb::cf { INLINE KeyPerFileConfigurationSource::KeyPerFileConfigurationSource(std::filesystem::path directoryPath, bool isOptional, std::string ignorePrefix) - : _directoryPath(std::move(directoryPath)), _isOptional(isOptional), _ignorePrefix(std::move(ignorePrefix)) + : _directoryPath(std::move(directoryPath)), _ignorePrefix(std::move(ignorePrefix)), _isOptional(isOptional) { } INLINE KeyPerFileConfigurationSource::KeyPerFileConfigurationSource( std::filesystem::path directoryPath, bool isOptional, std::function ignoreCondition) - : _directoryPath(std::move(directoryPath)), _isOptional(isOptional), - _ignoreCondition(std::move(ignoreCondition)) + : _directoryPath(std::move(directoryPath)), _ignoreCondition(std::move(ignoreCondition)), + _isOptional(isOptional) { } @@ -65,31 +65,52 @@ namespace sb::cf } for (auto const &entry : std::filesystem::directory_iterator{directoryPath}) { - const auto &filePath = entry.path(); - if (!entry.is_regular_file() || canIgnore(filePath)) + if (auto source = tryGetMappedFileSource(entry)) { - continue; + sources.add(source); } - auto extension = filePath.extension(); + } + return sources.build(builder); + } + + INLINE IConfigurationSource::SPtr KeyPerFileConfigurationSource::tryGetMappedFileSource( + const std::filesystem::directory_entry &entry) const + { + if (const auto &filePath = entry.path(); entry.is_regular_file()) + { + if (auto fileSource = tryGetFileSource(filePath)) + { + auto mapFcn = [name = filePath.stem().generic_string()](JsonObject &&config) -> JsonObject { + auto res = JsonObject{}; + const auto keys = details::StringUtils::split(name, "__"); + details::JsonExt::updateWith(res, keys, std::move(config)); + return res; + }; + return MapConfigurationSource::create(std::move(fileSource), std::move(mapFcn)); + } + } + return nullptr; + } + + INLINE IConfigurationSource::SPtr KeyPerFileConfigurationSource::tryGetFileSource( + const std::filesystem::path &filePath) const + { + if (!canIgnore(filePath)) + { + const auto &extension = filePath.extension(); if (extension == ".json") { - auto fileSource = JsonFileConfigurationSource::create(filePath); - auto mapSource = MapConfigurationSource::create( - std::move(fileSource), [name = filePath.stem().generic_string()](JsonObject config) -> JsonObject { - auto res = JsonObject{}; - details::JsonExt::deepGetOrOverride(res, details::utils::split(name, "__")) = std::move(config); - return res; - }); - sources.add(mapSource); + return JsonFileConfigurationSource::create(filePath); } } - return sources.build(builder); + return nullptr; } INLINE bool KeyPerFileConfigurationSource::canIgnore(const std::filesystem::path &filePath) const { auto &ignorePrefix = getIgnorePrefix(); - if (!ignorePrefix.empty() && details::utils::startsWith(filePath.filename().generic_string(), ignorePrefix)) + if (!ignorePrefix.empty() && + details::StringUtils::startsWith(filePath.filename().generic_string(), ignorePrefix)) { return true; } @@ -100,5 +121,4 @@ namespace sb::cf } return false; } - } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp index 96f17fc..2080b27 100644 --- a/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp @@ -1,24 +1,24 @@ #pragma once -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Sources/MapConfiguration.hpp" namespace sb::cf { INLINE MapConfigurationSource::MapConfigurationSource(IConfigurationSource::SPtr source, - std::function mapFcn) + std::function mapFcn) : _source(std::move(source)), _mapFcn(std::move(mapFcn)) { - details::utils::assertPtr(_source); + details::Require::notNull(_source); } INLINE MapConfigurationSource::SPtr MapConfigurationSource::create(IConfigurationSource::SPtr source, - std::function mapFcn) + std::function mapFcn) { return MapConfigurationSource::SPtr(new MapConfigurationSource{std::move(source), std::move(mapFcn)}); } - INLINE const std::function &MapConfigurationSource::getMapFcn() const { return _mapFcn; } + INLINE const std::function &MapConfigurationSource::getMapFcn() const { return _mapFcn; } INLINE IConfigurationProvider::Ptr MapConfigurationSource::build(IConfigurationBuilder &builder) { @@ -29,14 +29,14 @@ namespace sb::cf IConfigurationProvider::Ptr innerProvider) : _source(std::move(source)), _innerProvider(std::move(innerProvider)) { - details::utils::assertPtr(_source); - details::utils::assertPtr(_innerProvider); + details::Require::notNull(_source); + details::Require::notNull(_innerProvider); } INLINE void MapConfigurationProvider::load() { - details::utils::assertPtr(_source); - details::utils::assertPtr(_innerProvider); + details::Require::notNull(_source); + details::Require::notNull(_innerProvider); _innerProvider->load(); set(_source->getMapFcn()(std::move(_innerProvider->getConfiguration()))); } diff --git a/Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp index 67723d3..96910f8 100644 --- a/Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/KeyPerFileConfiguration.hpp @@ -47,6 +47,11 @@ namespace sb::cf IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; private: + [[nodiscard]] IConfigurationSource::SPtr tryGetMappedFileSource( + std::filesystem::directory_entry const &entry) const; + + [[nodiscard]] IConfigurationSource::SPtr tryGetFileSource(const std::filesystem::path &filePath) const; + [[nodiscard]] bool canIgnore(const std::filesystem::path &filePath) const; }; } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/MapConfiguration.hpp b/Include/SevenBit/Conf/Sources/MapConfiguration.hpp index 2371ec8..886012b 100644 --- a/Include/SevenBit/Conf/Sources/MapConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/MapConfiguration.hpp @@ -16,18 +16,18 @@ namespace sb::cf { private: IConfigurationSource::SPtr _source; - std::function _mapFcn; + std::function _mapFcn; - MapConfigurationSource(IConfigurationSource::SPtr source, std::function mapFcn); + MapConfigurationSource(IConfigurationSource::SPtr source, std::function mapFcn); public: using Ptr = std::unique_ptr; using SPtr = std::shared_ptr; [[nodiscard]] static SPtr create(IConfigurationSource::SPtr source, - std::function mapFcn); + std::function mapFcn); - [[nodiscard]] const std::function &getMapFcn() const; + [[nodiscard]] const std::function &getMapFcn() const; IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; }; diff --git a/Tests/Unit/SettingSplitterTest.cpp b/Tests/Unit/SettingSplitterTest.cpp index 8576a9b..a1d6571 100644 --- a/Tests/Unit/SettingSplitterTest.cpp +++ b/Tests/Unit/SettingSplitterTest.cpp @@ -2,7 +2,7 @@ // #include // // #include "SevenBit/Conf/Details/SettingSplitter.hpp" -// #include "SevenBit/Conf/Details/Utils.hpp" +// #include "SevenBit/Conf/Details/StringUtils.hpp" // #include "Utilities/ParamsTest.hpp" // // diff --git a/Tests/Unit/UtilsTest.cpp b/Tests/Unit/UtilsTest.cpp index 6cfc816..1122f78 100644 --- a/Tests/Unit/UtilsTest.cpp +++ b/Tests/Unit/UtilsTest.cpp @@ -1,7 +1,7 @@ #include #include -#include "SevenBit/Conf/Details/Utils.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" #include "Utilities/ParamsTest.hpp" class UtilsTest : public testing::Test @@ -26,7 +26,7 @@ Params CheckNumberStringsData{ PARAMS_TEST(UtilsTest, ShouldCheckNumberStrings, CheckNumberStringsData) { auto &[string, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::isNumberString(string), expected); + EXPECT_EQ(sb::cf::details::StringUtils::isNumber(string), expected); } Params IgnoreCaseLessData{ @@ -46,7 +46,7 @@ Params IgnoreCaseLessData{ PARAMS_TEST(UtilsTest, ShouldIgnoreCaseLessCompareStrings, IgnoreCaseLessData) { auto &[string, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::ignoreCaseLess(string, search), expected); + EXPECT_EQ(sb::cf::details::StringUtils::ignoreCaseLess(string, search), expected); } Params IgnoreCaseEqualsData{ @@ -66,7 +66,7 @@ Params IgnoreCaseEqualsData{ PARAMS_TEST(UtilsTest, ShouldIgnoreCaseCompareStrings, IgnoreCaseEqualsData) { auto &[string, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::ignoreCaseEqual(string, search), expected); + EXPECT_EQ(sb::cf::details::StringUtils::ignoreCaseEqual(string, search), expected); } Params ContainsAtData{ @@ -81,7 +81,7 @@ Params ContainsAtData{ PARAMS_TEST(UtilsTest, ShouldContainsAt, ContainsAtData) { auto &[string, index, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::containsAt(string, index, search), expected); + EXPECT_EQ(sb::cf::details::StringUtils::containsAt(string, index, search), expected); } Params, std::optional> ContainsAtMultitData{ @@ -107,7 +107,7 @@ Params, std::optional ContainsAtFromEndData{ @@ -121,7 +121,7 @@ Params ContainsAtFromEndData{ PARAMS_TEST(UtilsTest, ShouldContainsAtFromEnd, ContainsAtFromEndData) { auto &[string, index, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::containsAtFromEnd(string, index, search), expected); + EXPECT_EQ(sb::cf::details::StringUtils::containsAtFromEnd(string, index, search), expected); } Params, std::optional> @@ -150,7 +150,7 @@ Params, std::optional StartsWithData{ @@ -162,7 +162,7 @@ Params StartsWithData{ PARAMS_TEST(UtilsTest, ShouldStartsWith, StartsWithData) { auto &[string, search, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::startsWith(string, search), expected); + EXPECT_EQ(sb::cf::details::StringUtils::startsWith(string, search), expected); } Params> SplitStrData{ @@ -181,7 +181,7 @@ Params> SplitStrData{ PARAMS_TEST(UtilsTest, ShouldSplitString, SplitStrData) { auto &[string, delim, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::split(string, delim), expected); + EXPECT_EQ(sb::cf::details::StringUtils::split(string, delim), expected); } Params, std::vector> SplitStrMultiData{ @@ -204,7 +204,7 @@ Params, std::vector PARAMS_TEST(UtilsTest, ShouldSplitStringMulti, SplitStrMultiData) { auto &[string, delims, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::split(string, delims), expected); + EXPECT_EQ(sb::cf::details::StringUtils::split(string, delims), expected); } Params, std::optional>> @@ -225,7 +225,7 @@ Params, std::optional, std::optional>> @@ -246,8 +246,8 @@ Params, std::optional, std::string, std::string> JoinStrData{ @@ -265,14 +265,7 @@ Params, std::string, std::string> JoinStrData{ PARAMS_TEST(UtilsTest, ShouldJoinStrings, JoinStrData) { auto &[strings, delim, expected] = GetParam(); - EXPECT_EQ(sb::cf::details::utils::joinViews(strings, delim), expected); -} - -TEST_F(UtilsTest, ShouldAssertPtr) -{ - int i = 1; - EXPECT_NO_THROW(sb::cf::details::utils::assertPtr(&i)); - EXPECT_THROW(sb::cf::details::utils::assertPtr(nullptr), sb::cf::NullPointerException); + EXPECT_EQ(sb::cf::details::StringUtils::join(strings, delim), expected); } Params> ConvertToNumberIntData{ @@ -282,7 +275,7 @@ Params> ConvertToNumberIntData{ PARAMS_TEST(UtilsTest, ShouldConvertToIntNumber, ConvertToNumberIntData) { auto &[string, full, expected] = GetParam(); - auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); + auto [success, result] = sb::cf::details::StringUtils::tryConvertTo(string, full); EXPECT_EQ(success, expected.first); if (success) @@ -299,7 +292,7 @@ Params> ConvertToNumberDoubleDat PARAMS_TEST(UtilsTest, ShouldConvertToDoubleNumber, ConvertToNumberDoubleData) { auto &[string, full, expected] = GetParam(); - auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); + auto [success, result] = sb::cf::details::StringUtils::tryConvertTo(string, full); EXPECT_EQ(success, expected.first); if (success) @@ -317,7 +310,7 @@ Params> ConvertToBoolData{ PARAMS_TEST(UtilsTest, ShouldConvertToBool, ConvertToBoolData) { auto &[string, full, expected] = GetParam(); - auto [success, result] = sb::cf::details::utils::tryStringTo(string, full); + auto [success, result] = sb::cf::details::StringUtils::tryConvertTo(string, full); EXPECT_EQ(success, expected.first); if (success) From 7238f20cc7b97d295ff43b9d7329a8e91e89885e Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Wed, 21 Feb 2024 22:04:59 +0100 Subject: [PATCH 07/31] update tests --- .../Conf/CommandLineParserBuilder.hpp | 2 + .../Conf/Details/CommandLineParser.hpp | 2 +- .../SevenBit/Conf/Details/Configuration.hpp | 4 +- .../SevenBit/Conf/Details/ContainerUtils.hpp | 2 - .../Conf/Details/DefaultDeserializers.hpp | 19 +- .../Conf/Details/EnvironmentVarsParser.hpp | 1 - .../Conf/Details/Impl/Configuration.hpp | 32 +- .../Details/Impl/DefaultDeserializers.hpp | 18 + .../Conf/Details/Impl/Deserializers.hpp | 2 +- .../Details/Impl/EnvironmentVarsParser.hpp | 1 + .../Conf/Details/Impl/JsonObjectExt.hpp | 16 +- .../Conf/Details/Impl/SettingSplitter.hpp | 2 +- .../Conf/Details/Impl/StringUtils.hpp | 12 +- Include/SevenBit/Conf/Details/JsonExt.hpp | 13 +- .../Conf/EnvironmentVarsParserBuilder.hpp | 2 + .../Conf/Impl/CommandLineParserBuilder.hpp | 18 +- .../Conf/Impl/ConfigurationBuilder.hpp | 2 +- .../Impl/EnvironmentVarsParserBuilder.hpp | 24 +- .../Sources/ConfigurationProviderBase.hpp | 6 +- .../Impl/ConfigurationProviderBase.hpp | 10 +- .../Impl/EnvironmentVarsConfiguration.hpp | 7 +- .../Sources/Impl/InMemoryConfiguration.hpp | 4 +- .../Conf/Sources/Impl/JsonConfiguration.hpp | 6 +- .../Sources/Impl/JsonFileConfiguration.hpp | 9 +- Source/Source.cpp | 3 +- Tests/Unit/CommandLineParserTest.cpp | 191 +++++++ Tests/Unit/DeserializersTest.cpp | 483 +++++++++--------- Tests/Unit/SettingParserTest.cpp | 55 +- Tests/Unit/SettingSplitterTest.cpp | 238 ++++----- 29 files changed, 714 insertions(+), 470 deletions(-) create mode 100644 Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp create mode 100644 Tests/Unit/CommandLineParserTest.cpp diff --git a/Include/SevenBit/Conf/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/CommandLineParserBuilder.hpp index 3fdfdd9..df08fbf 100644 --- a/Include/SevenBit/Conf/CommandLineParserBuilder.hpp +++ b/Include/SevenBit/Conf/CommandLineParserBuilder.hpp @@ -41,6 +41,8 @@ namespace sb::cf IValueDeserializersMap::Ptr getValueDeserializersMap(); + std::vector> getValueDeserializers(); + CommandLineParserConfig &getConfig(); }; diff --git a/Include/SevenBit/Conf/Details/CommandLineParser.hpp b/Include/SevenBit/Conf/Details/CommandLineParser.hpp index 22e7e7d..80d3a43 100644 --- a/Include/SevenBit/Conf/Details/CommandLineParser.hpp +++ b/Include/SevenBit/Conf/Details/CommandLineParser.hpp @@ -29,7 +29,7 @@ namespace sb::cf::details using Ptr = std::unique_ptr; CommandLineParser(ISettingSplitter::Ptr optionSplitter, IValueDeserializersMap::Ptr valueDeserializersMap, - std::vector optionPrefixes, bool considerSeparated); + std::vector optionPrefixes, bool considerSeparated = true); [[nodiscard]] JsonObject parse(const std::vector &arguments) const override; diff --git a/Include/SevenBit/Conf/Details/Configuration.hpp b/Include/SevenBit/Conf/Details/Configuration.hpp index c992b4a..b28bb0c 100644 --- a/Include/SevenBit/Conf/Details/Configuration.hpp +++ b/Include/SevenBit/Conf/Details/Configuration.hpp @@ -11,7 +11,7 @@ #include "SevenBit/Conf/IConfigurationProvider.hpp" #include "SevenBit/Conf/Json.hpp" -namespace sb::cf +namespace sb::cf::details { class EXPORT Configuration : public IConfiguration { @@ -108,7 +108,7 @@ namespace sb::cf [[nodiscard]] JsonValue &throwNotFoundException(std::string_view key) const; }; -} // namespace sb::cf +} // namespace sb::cf::details #ifdef _7BIT_CONF_ADD_IMPL #include "SevenBit/Conf/Details/Impl/Configuration.hpp" diff --git a/Include/SevenBit/Conf/Details/ContainerUtils.hpp b/Include/SevenBit/Conf/Details/ContainerUtils.hpp index f9a2ae9..4e63e3a 100644 --- a/Include/SevenBit/Conf/Details/ContainerUtils.hpp +++ b/Include/SevenBit/Conf/Details/ContainerUtils.hpp @@ -5,8 +5,6 @@ #include "SevenBit/Conf/LibraryConfig.hpp" -#include "SevenBit/Conf/Exceptions.hpp" - namespace sb::cf::details { struct ContainerUtils diff --git a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp index bb25f55..ee037dd 100644 --- a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp +++ b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp @@ -1,20 +1,17 @@ #pragma once +#include "SevenBit/Conf/LibraryConfig.hpp" + #include "SevenBit/Conf/Details/Deserializers.hpp" -namespace sb::cf +namespace sb::cf::details { struct DefaultDeserializers { - static void add(std::vector> &deserializers) - { - deserializers.emplace_back("string", std::make_unique()); - deserializers.emplace_back("bool", std::make_unique()); - deserializers.emplace_back("int", std::make_unique()); - deserializers.emplace_back("double", std::make_unique()); - deserializers.emplace_back("uint", std::make_unique()); - deserializers.emplace_back("json", std::make_unique()); - deserializers.emplace_back("null", std::make_unique()); - } + static void add(std::vector> &deserializers); }; } // namespace sb::cf + +#ifdef _7BIT_CONF_ADD_IMPL +#include "SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp" +#endif diff --git a/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp index 1663877..6c47ce3 100644 --- a/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp +++ b/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp index fed0edb..b609f61 100644 --- a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp @@ -8,7 +8,7 @@ #include "SevenBit/Conf/Details/StringUtils.hpp" #include "SevenBit/Conf/Exceptions.hpp" -namespace sb::cf +namespace sb::cf::details { INLINE Configuration::Configuration(std::vector providers) @@ -34,31 +34,25 @@ namespace sb::cf INLINE JsonValue &Configuration::at(const std::string &key) { return rootAsObject().at(key); } - INLINE const JsonValue *Configuration::find(std::string_view key) const - { - return details::JsonExt::find(root(), key); - } + INLINE const JsonValue *Configuration::find(std::string_view key) const { return JsonExt::find(root(), key); } - INLINE JsonValue *Configuration::find(std::string_view key) { return details::JsonExt::find(root(), key); } + INLINE JsonValue *Configuration::find(std::string_view key) { return JsonExt::find(root(), key); } INLINE const JsonValue *Configuration::deepFind(std::string_view key) const { - return details::JsonExt::deepFind(rootAsObject(), key); + return JsonExt::deepFind(rootAsObject(), key); } - INLINE JsonValue *Configuration::deepFind(std::string_view key) - { - return details::JsonExt::deepFind(rootAsObject(), key); - } + INLINE JsonValue *Configuration::deepFind(std::string_view key) { return JsonExt::deepFind(rootAsObject(), key); } INLINE const JsonValue *Configuration::deepFind(const std::vector &key) const { - return details::JsonExt::deepFind(rootAsObject(), key); + return JsonExt::deepFind(rootAsObject(), key); } INLINE JsonValue *Configuration::deepFind(const std::vector &key) { - return details::JsonExt::deepFind(rootAsObject(), key); + return JsonExt::deepFind(rootAsObject(), key); } INLINE JsonValue &Configuration::deepAt(std::string_view key) @@ -87,14 +81,14 @@ namespace sb::cf INLINE JsonValue &Configuration::operator[](std::string_view key) { - return details::JsonExt::deepGetOrOverride(rootAsObject(), key); + return JsonExt::deepGetOrOverride(rootAsObject(), key); } INLINE const JsonValue &Configuration::operator[](std::string_view key) const { return deepAt(key); } INLINE JsonValue &Configuration::operator[](const std::vector &key) { - return details::JsonExt::deepGetOrOverride(rootAsObject(), key); + return JsonExt::deepGetOrOverride(rootAsObject(), key); } INLINE const JsonValue &Configuration::operator[](const std::vector &key) const @@ -108,9 +102,9 @@ namespace sb::cf configRoot.clear(); for (auto &provider : _providers) { - details::Require::notNull(provider); + Require::notNull(provider); provider->load(); - details::JsonExt::deepMerge(configRoot, std::move(provider->getConfiguration())); + JsonExt::deepMerge(configRoot, std::move(provider->getConfiguration())); } } @@ -120,11 +114,11 @@ namespace sb::cf INLINE JsonValue &Configuration::throwNotFoundException(const std::vector &key) const { - throw ValueNotFoundException{"Value was not found for key: " + details::StringUtils::join(key, ":")}; + throw ValueNotFoundException{"Value was not found for key: " + StringUtils::join(key, ":")}; } INLINE JsonValue &Configuration::throwNotFoundException(std::string_view key) const { throw ValueNotFoundException{"Value was not found for key: " + std::string{key}}; } -} // namespace sb::cf +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp new file mode 100644 index 0000000..29223c9 --- /dev/null +++ b/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "SevenBit/Conf/Details/DefaultDeserializers.hpp" + +namespace sb::cf::details +{ + INLINE void DefaultDeserializers::add(std::vector> &deserializers) + { + deserializers.emplace_back("string", std::make_unique()); + deserializers.emplace_back("bool", std::make_unique()); + deserializers.emplace_back("int", std::make_unique()); + deserializers.emplace_back("double", std::make_unique()); + deserializers.emplace_back("uint", std::make_unique()); + deserializers.emplace_back("json", std::make_unique()); + deserializers.emplace_back("null", std::make_unique()); + } + +} // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp b/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp index e9414f1..14d28e5 100644 --- a/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Deserializers.hpp @@ -33,7 +33,7 @@ namespace sb::cf::details return value ? StringUtils::convertTo(*value) : 0.0; } - INLINE JsonValue NullDeserializer::deserialize(std::optional value) const { return json::null; } + INLINE JsonValue NullDeserializer::deserialize(std::optional) const { return json::null; } INLINE JsonValue JsonDeserializer::deserialize(std::optional value) const { diff --git a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp index ac86380..1d47bac 100644 --- a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp +++ b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp @@ -3,6 +3,7 @@ #include #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" +#include "SevenBit/Conf/Details/JsonExt.hpp" #include "SevenBit/Conf/Details/Require.hpp" #include "SevenBit/Conf/Exceptions.hpp" diff --git a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp b/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp index 0482585..1450a8e 100644 --- a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp +++ b/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp @@ -194,7 +194,7 @@ namespace sb::cf::details return *current; } - INLINE void JsonExt::deepMerge(JsonValue &json, JsonValue override) + INLINE void JsonExt::deepMerge(JsonValue &json, JsonValue &&override) { if (override.is_uninitialized()) // undefined is mark for skip { @@ -214,7 +214,7 @@ namespace sb::cf::details } } - INLINE void JsonExt::deepMerge(JsonArray &json, JsonArray override) + INLINE void JsonExt::deepMerge(JsonArray &json, JsonArray &&override) { if (json.empty()) { @@ -231,7 +231,7 @@ namespace sb::cf::details } } - INLINE void JsonExt::deepMerge(JsonObject &json, JsonObject override) + INLINE void JsonExt::deepMerge(JsonObject &json, JsonObject &&override) { if (json.empty()) { @@ -254,6 +254,16 @@ namespace sb::cf::details deepGetOrOverride(json, keys) = std::move(value); } + INLINE void JsonExt::updateWith(JsonObject &json, std::string_view key, JsonValue &&value) + { + deepGetOrOverride(json, key) = std::move(value); + } + + INLINE void JsonExt::updateWith(JsonObject &json, std::string_view key, JsonObject &&value) + { + deepGetOrOverride(json, key) = std::move(value); + } + INLINE void JsonExt::checkSegmentSize(const std::vector &key) { if (key.empty()) diff --git a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp index d92f134..d5c261f 100644 --- a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp @@ -17,7 +17,7 @@ namespace sb::cf::details INLINE ISettingSplitter::Result SettingSplitter::split(const std::string_view setting) const { auto [rawKey, value] = splitSetting(setting); - auto type = tryExtractType(rawKey); + const auto type = tryExtractType(rawKey); return {splitKey(rawKey), type, value}; } diff --git a/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp b/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp index 3495362..ba07fb6 100644 --- a/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp +++ b/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp @@ -109,10 +109,10 @@ namespace sb::cf::details std::vector result; for (int i = 0; i < str.size(); ++i) { - if (auto foundDelim = containsAt(str, i, separators)) + if (const auto separator = containsAt(str, i, separators)) { result.emplace_back(str.substr(0, i)); - str.remove_prefix(foundDelim->size() + result.back().size()); + str.remove_prefix(separator->size() + result.back().size()); i = -1; } } @@ -125,9 +125,9 @@ namespace sb::cf::details { for (size_t i = 0; i < str.size(); ++i) { - if (auto foundDelim = containsAt(str, i, separators)) + if (const auto separator = containsAt(str, i, separators)) { - return std::make_pair(str.substr(0, i), str.substr(i + foundDelim->size())); + return std::make_pair(str.substr(0, i), str.substr(i + separator->size())); } } return std::nullopt; @@ -138,9 +138,9 @@ namespace sb::cf::details { for (int i = static_cast(str.size()) - 1; i >= 0; --i) { - if (auto foundDelim = containsAtFromEnd(str, i, separators)) + if (const auto separator = containsAtFromEnd(str, i, separators)) { - return std::make_pair(str.substr(0, i + 1 - foundDelim->size()), str.substr(i + 1)); + return std::make_pair(str.substr(0, i + 1 - separator->size()), str.substr(i + 1)); } } return std::nullopt; diff --git a/Include/SevenBit/Conf/Details/JsonExt.hpp b/Include/SevenBit/Conf/Details/JsonExt.hpp index 6c9d8c5..f7dbfa4 100644 --- a/Include/SevenBit/Conf/Details/JsonExt.hpp +++ b/Include/SevenBit/Conf/Details/JsonExt.hpp @@ -43,23 +43,20 @@ namespace sb::cf::details [[nodiscard]] static const JsonValue *deepFind(const JsonArray &json, const std::vector &key); static JsonValue &getOrOverride(JsonValue &json, std::string_view key); - static JsonValue &getOrOverride(JsonObject &json, std::string_view key); - static JsonValue &getOrOverride(JsonArray &array, size_t index); static JsonValue &deepGetOrOverride(JsonObject &json, std::string_view key); - static JsonValue &deepGetOrOverride(JsonObject &json, const std::vector &keys); - static void deepMerge(JsonValue &json, JsonValue override); - - static void deepMerge(JsonArray &json, JsonArray override); - - static void deepMerge(JsonObject &json, JsonObject override); + static void deepMerge(JsonValue &json, JsonValue &&override); + static void deepMerge(JsonArray &json, JsonArray &&override); + static void deepMerge(JsonObject &json, JsonObject &&override); + static void updateWith(JsonObject &json, std::string_view key, JsonValue &&value); static void updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value); + static void updateWith(JsonObject &json, std::string_view key, JsonObject &&value); static void updateWith(JsonObject &json, const std::vector &keys, JsonObject &&value); static void checkSegmentSize(const std::vector &key); diff --git a/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp b/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp index cf0eac6..fbb590e 100644 --- a/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp +++ b/Include/SevenBit/Conf/EnvironmentVarsParserBuilder.hpp @@ -41,6 +41,8 @@ namespace sb::cf IValueDeserializersMap::Ptr getValueDeserializersMap(); + std::vector> getValueDeserializers(); + EnvironmentVarsParserConfig &getConfig(); }; diff --git a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp index ff106d7..fa38dfb 100644 --- a/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/CommandLineParserBuilder.hpp @@ -41,7 +41,7 @@ namespace sb::cf INLINE CommandLineParserBuilder &CommandLineParserBuilder::useDefaultValueDeserializers() { - DefaultDeserializers::add(_valueDeserializers); + details::DefaultDeserializers::add(_valueDeserializers); return *this; } @@ -71,17 +71,23 @@ namespace sb::cf { if (!_valueDeserializersMap) { - if (_valueDeserializers.empty()) - { - useDefaultValueDeserializers(); - } auto &config = getConfig(); useValueDeserializersMap(std::make_unique( - config.defaultType, config.throwOnUnknownType, std::move(_valueDeserializers))); + config.defaultType, config.throwOnUnknownType, getValueDeserializers())); } return std::move(_valueDeserializersMap); } + INLINE std::vector> CommandLineParserBuilder:: + getValueDeserializers() + { + if (_valueDeserializers.empty()) + { + useDefaultValueDeserializers(); + } + return std::move(_valueDeserializers); + } + INLINE CommandLineParserConfig &CommandLineParserBuilder::getConfig() { if (!_config) diff --git a/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp b/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp index cab7e7b..47093e7 100644 --- a/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/ConfigurationBuilder.hpp @@ -31,7 +31,7 @@ namespace sb::cf details::Require::notNull(source); providers.emplace_back(source->build(*this)); } - return std::make_unique(std::move(providers)); + return std::make_unique(std::move(providers)); } INLINE void ConfigurationBuilder::clear() { _sources.clear(); } diff --git a/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp b/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp index 956388f..efb8ee2 100644 --- a/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp +++ b/Include/SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp @@ -36,7 +36,7 @@ namespace sb::cf INLINE EnvironmentVarsParserBuilder &EnvironmentVarsParserBuilder::useDefaultValueDeserializers() { - DefaultDeserializers::add(_valueDeserializers); + details::DefaultDeserializers::add(_valueDeserializers); return *this; } @@ -50,9 +50,9 @@ namespace sb::cf if (!_splitter) { auto &config = getConfig(); - useSplitter( - std::make_unique(std::move(config.variableSplitters), std::move(config.typeMarkers), - std::move(config.keySplitters), config.allowEmptyKeys)); + useSplitter(std::make_unique( + std::move(config.variableSplitters), std::move(config.typeMarkers), std::move(config.keySplitters), + config.allowEmptyKeys)); } return std::move(_splitter); } @@ -61,17 +61,23 @@ namespace sb::cf { if (!_valueDeserializersMap) { - if (_valueDeserializers.empty()) - { - useDefaultValueDeserializers(); - } auto &config = getConfig(); useValueDeserializersMap(std::make_unique( - config.defaultType, config.throwOnUnknownType, std::move(_valueDeserializers))); + config.defaultType, config.throwOnUnknownType, getValueDeserializers())); } return std::move(_valueDeserializersMap); } + INLINE std::vector> EnvironmentVarsParserBuilder:: + getValueDeserializers() + { + if (_valueDeserializers.empty()) + { + useDefaultValueDeserializers(); + } + return std::move(_valueDeserializers); + } + INLINE EnvironmentVarsParserConfig &EnvironmentVarsParserBuilder::getConfig() { if (!_config) diff --git a/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp b/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp index 7352527..519a0b3 100644 --- a/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp +++ b/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp @@ -23,11 +23,11 @@ namespace sb::cf protected: void clear(); - void set(JsonObject configuration); + void set(JsonObject &&configuration); - void update(JsonObject configuration); + void update(JsonObject &&configuration); - void update(const std::vector &keys, JsonValue value); + void update(const std::vector &keys, JsonValue &&value); }; } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp b/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp index 7b412f6..0ea1644 100644 --- a/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp @@ -6,7 +6,6 @@ #include "SevenBit/Conf/Exceptions.hpp" #include "SevenBit/Conf/Sources/ConfigurationProviderBase.hpp" - namespace sb::cf { INLINE const JsonObject &ConfigurationProviderBase::getConfiguration() const { return _configuration; } @@ -15,14 +14,17 @@ namespace sb::cf INLINE void ConfigurationProviderBase::clear() { _configuration.clear(); } - INLINE void ConfigurationProviderBase::set(JsonObject configuration) { _configuration = std::move(configuration); } + INLINE void ConfigurationProviderBase::set(JsonObject &&configuration) + { + _configuration = std::move(configuration); + } - INLINE void ConfigurationProviderBase::update(JsonObject configuration) + INLINE void ConfigurationProviderBase::update(JsonObject &&configuration) { details::JsonExt::deepMerge(_configuration, std::move(configuration)); } - INLINE void ConfigurationProviderBase::update(const std::vector &keys, JsonValue value) + INLINE void ConfigurationProviderBase::update(const std::vector &keys, JsonValue &&value) { details::JsonExt::updateWith(_configuration, keys, std::move(value)); } diff --git a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp index 3b0a45e..18f6c00 100644 --- a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp @@ -58,12 +58,7 @@ namespace sb::cf auto &prefix = _source->getPrefix(); for (auto env = _7BIT_CONF_ENV_PTR; *env; env++) { - std::string_view envStr = *env; - if (prefix.empty()) - { - result.push_back(envStr); - } - else if (details::StringUtils::startsWith(envStr, prefix)) + if (std::string_view envStr = *env; prefix.empty() || details::StringUtils::startsWith(envStr, prefix)) { result.push_back(envStr.substr(prefix.size())); } diff --git a/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp index 962ef12..24470b9 100644 --- a/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp @@ -32,10 +32,10 @@ namespace sb::cf INLINE void InMemoryConfigurationProvider::load() { clear(); - for (auto &[key, value] : *_source) + for (auto [key, value] : *_source) { JsonObject result{}; - details::JsonExt::deepGetOrOverride(result, key) = std::move(value); + details::JsonExt::updateWith(result, key, std::move(value)); update(std::move(result)); } } diff --git a/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp index f59bb94..adf2303 100644 --- a/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp @@ -28,5 +28,9 @@ namespace sb::cf details::Require::notNull(_source); } - INLINE void JsonConfigurationProvider::load() { set(_source->getJson()); } + INLINE void JsonConfigurationProvider::load() + { + auto copy = _source->getJson(); + set(std::move(copy)); + } } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp index 81b7354..8135ec1 100644 --- a/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonFileConfiguration.hpp @@ -39,18 +39,19 @@ namespace sb::cf INLINE JsonObject JsonFileConfigurationProvider::getJsonFromFile() { - if (!std::filesystem::exists(_source->getFilePath())) + auto &filePath = _source->getFilePath(); + if (!std::filesystem::exists(filePath)) { if (_source->getIsOptional()) { return JsonObject{}; } - throw ConfigFileNotFoundException(_source->getFilePath()); + throw ConfigFileNotFoundException(filePath); } - auto json = tao::json::basic_from_file(_source->getFilePath()); + auto json = tao::json::basic_from_file(filePath); if (!json.is_object()) { - throw BadConfigFileException(_source->getFilePath(), "file does not contain json object"); + throw BadConfigFileException(filePath, "file does not contain json object"); } return json.get_object(); } diff --git a/Source/Source.cpp b/Source/Source.cpp index af52a0b..fb7785d 100644 --- a/Source/Source.cpp +++ b/Source/Source.cpp @@ -3,11 +3,12 @@ #include "SevenBit/Conf/Details/Impl/CommandLineParser.hpp" #include "SevenBit/Conf/Details/Impl/Configuration.hpp" +#include "SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp" #include "SevenBit/Conf/Details/Impl/Deserializers.hpp" #include "SevenBit/Conf/Details/Impl/JsonObjectExt.hpp" #include "SevenBit/Conf/Details/Impl/SettingParser.hpp" -#include "SevenBit/Conf/Details/Impl/SettingsParser.hpp" #include "SevenBit/Conf/Details/Impl/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/Impl/SettingsParser.hpp" #include "SevenBit/Conf/Details/Impl/Utils.hpp" #include "SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp" #include "SevenBit/Conf/Impl/ConfigurationBuilder.hpp" diff --git a/Tests/Unit/CommandLineParserTest.cpp b/Tests/Unit/CommandLineParserTest.cpp new file mode 100644 index 0000000..f175f46 --- /dev/null +++ b/Tests/Unit/CommandLineParserTest.cpp @@ -0,0 +1,191 @@ +// #include +// #include +// #include +// +// #include "Mocks/DeserializerMock.hpp" +// #include "Mocks/SettingSplitterMock.hpp" +// #include "Mocks/ValueDeserializersMapMock.hpp" +// #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" +// #include "SevenBit/Conf/Exceptions.hpp" +// +// class EnvironmentVarsParserTest : public testing::Test +// { +// protected: +// static void TearUpTestSuite() {} +// +// EnvironmentVarsParserTest() {} +// +// void SetUp() override {} +// +// void TearDown() override {} +// +// static void TearDownTestSuite() {} +// }; +// +// TEST_F(EnvironmentVarsParserTest, ShouldParseSetting) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!int=123"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = 123; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, +// true}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_FALSE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullSplitter) +// { +// sb::cf::ISettingSplitter::Ptr splitter; +// auto deserializers = std::make_unique(); +// +// EXPECT_THROW( +// (sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers), "string", false, +// true}), sb::cf::ConfigException); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullDeserializers) +// { +// sb::cf::IValueDeserializersMap::Ptr deserializers; +// auto splitter = std::make_unique(); +// +// EXPECT_THROW( +// (sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers), "string", false, +// true}), sb::cf::ConfigException); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = "value"; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, +// true}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); +// EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--!string=value"; +// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = "value"; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// true}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueEmptyKey) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--!string=value"; +// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, +// true}; +// +// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_FALSE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultTypeForUnknownType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!unknown=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor) +// .WillOnce(testing::Return(nullptr)) +// .WillOnce(testing::Return(&deserializer)); +// sb::cf::JsonValue returnedValue = "value"; +// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// false}; +// +// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_FALSE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!unknown=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// true}; +// +// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_TRUE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } +// +// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownDefaultType) +// { +// DeserializerMock deserializer; +// auto deserializers = std::make_unique(); +// auto splitter = std::make_unique(); +// +// std::string_view setting = "--option:deep:deep!unknown=value"; +// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; +// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); +// EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); +// sb::cf::JsonValue returnedValue = "value"; +// +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// false}; +// +// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); +// EXPECT_EQ(parser.getDefaultType(), "string"); +// EXPECT_FALSE(parser.getThrowOnUnknownType()); +// EXPECT_TRUE(parser.getAllowEmptyKeys()); +// } diff --git a/Tests/Unit/DeserializersTest.cpp b/Tests/Unit/DeserializersTest.cpp index 9fb5536..a39e759 100644 --- a/Tests/Unit/DeserializersTest.cpp +++ b/Tests/Unit/DeserializersTest.cpp @@ -1,237 +1,246 @@ -// #include -// #include -// #include -// #include -// #include -// #include -// -// #include "SevenBit/Conf/Details/Deserializers.hpp" -// #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -// #include "Utilities/ParamsTest.hpp" -// -// class DeserializersTest : public testing::Test -// { -// protected: -// static void TearUpTestSuite() {} -// -// DeserializersTest() {} -// -// void SetUp() override {} -// -// void TearDown() override {} -// -// static void TearDownTestSuite() {} -// }; -// -// sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap() -// { -// sb::cf::details::ValueDeserializersMap deserializers; -// deserializers.set("string", std::make_unique()); -// deserializers.set("bool", std::make_unique()); -// deserializers.set("int", std::make_unique()); -// deserializers.set("double", std::make_unique()); -// deserializers.set("uint", std::make_unique()); -// deserializers.set("json", std::make_unique()); -// deserializers.set("null", std::make_unique()); -// return std::move(deserializers); -// } -// -// static Params, sb::cf::JsonValue> DeserializeData = { -// // Int -// {"int", "12", std::int64_t{12}}, -// {"int", "-12", std::int64_t{-12}}, -// {"Int", "-12", std::int64_t{-12}}, -// {"INT", "-12", std::int64_t{-12}}, -// {"InT", "-12", std::int64_t{-12}}, -// {"int", std::nullopt, std::int64_t{0}}, -// {"int", "1", std::int64_t{1}}, -// {"int", " 1", std::int64_t{1}}, -// {"int", "\t\n 1", std::int64_t{1}}, -// {"int", "1", std::int64_t{1}}, -// {"Int", "1", std::int64_t{1}}, -// {"InT", "1", std::int64_t{1}}, -// {"INT", "1", std::int64_t{1}}, -// {"int", "-1234", std::int64_t{-1234}}, -// {"int", "1234", std::int64_t{1234}}, -// {"int", "-223372036854775807", std::int64_t{-223372036854775807ll}}, -// {"int", "223372036854775807", std::int64_t{223372036854775807ll}}, -// -// // UInt -// {"uint", "12", std::uint64_t{12}}, -// {"Uint", "12", std::uint64_t{12}}, -// {"UINT", "12", std::uint64_t{12}}, -// {"UiNt", "12", std::uint64_t{12}}, -// {"uint", "1222", std::uint64_t{1222}}, -// {"uint", std::nullopt, std::uint64_t{0}}, -// {"uint", "1", std::uint64_t{1}}, -// {"uint", " 1", std::uint64_t{1}}, -// {"uint", "\t\n 1", std::uint64_t{1}}, -// {"uInt", "1", std::uint64_t{1}}, -// {"uInT", "1", std::uint64_t{1}}, -// {"UINT", "1", std::uint64_t{1}}, -// {"uint", "1234", std::uint64_t{1234}}, -// {"uint", "223372036854775807", std::uint64_t{223372036854775807ll}}, -// {"uint", "17446744073709551615", std::uint64_t{17446744073709551615ull}}, -// -// // Bool -// {"bool", "true", true}, -// {"BoOl", "true", true}, -// {"bool", "1", true}, -// {"bool", "-11", true}, -// {"bool", "0", false}, -// {"Bool", "true", true}, -// {"BOOL", "true", true}, -// {"bool", "True", true}, -// {"bool", "TrUe", true}, -// {"bool", " 1", true}, -// {"bool", "\t\n 1", true}, -// {"bool", "-1", true}, -// {"bool", "-1123", true}, -// {"bool", "1123", true}, -// {"bool", "false", false}, -// {"bool", "False", false}, -// {"bool", "FAlSe", false}, -// {"bool", std::nullopt, false}, -// // String -// {"string", "hello", std::string{"hello"}}, -// {"String", "hello", std::string{"hello"}}, -// {"STRING", "hello", std::string{"hello"}}, -// {"STRING", "hello", std::string{"hello"}}, -// {"String", "hello", std::string{"hello"}}, -// {"StRinG", "hell\to", std::string{"hell\to"}}, -// {"string", "hello", std::string{"hello"}}, -// {"string", "", std::string{""}}, -// {"string", std::nullopt, std::string{""}}, -// // Double -// {"double", "1.1", 1.1}, -// {"Double", "1.1", 1.1}, -// {"DOUBLE", "1.1", 1.1}, -// {"DoUBle", "1.1", 1.1}, -// {"double", "-11.1", -11.1}, -// {"double", "1", 1.0}, -// {"double", " 1", 1.0}, -// {"double", "\t\n 1", 1.0}, -// {"Double", "1", 1.0}, -// {"dOuBle", "1", 1.0}, -// {"DOUBLE", "1", 1.0}, -// {"double", "1.123", 1.123}, -// {"double", "-12.21", -12.21}, -// {"double", "10000.11", 10000.11}, -// {"double", "-10000.11", -10000.11}, -// {"double", std::nullopt, 0.0}, -// // Null -// {"null", "hello", sb::cf::json::null}, -// {"Null", "hello", sb::cf::json::null}, -// {"NULL", "hello", sb::cf::json::null}, -// {"nULl", "hello", sb::cf::json::null}, -// {"null", "1", sb::cf::json::null}, -// {"null", "asdqwdwq", sb::cf::json::null}, -// {"Null", "1", sb::cf::json::null}, -// {"NULL", "1asdqwdwq", sb::cf::json::null}, -// {"NuLl", "1.123 asdqwdwq", sb::cf::json::null}, -// {"null", std::nullopt, sb::cf::json::null}, -// // Json -// {"json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, -// {"Json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, -// {"JSON", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, -// {"jSOn", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, -// {"JSon", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, -// {"json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, -// {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, -// {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, -// {"JsOn", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, -// {"json", R"({"hello": [1]})", sb::cf::JsonObject{{"hello", sb::cf::JsonArray{1}}}}, -// {"json", R"({"hello": null})", sb::cf::JsonObject{{"hello", sb::cf::json::null}}}, -// -// }; -// PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) -// { -// const auto &[type, value, expected] = GetParam(); -// auto deserializers = makeDefaultDeserializersMap(); -// -// auto deserializer = deserializers.getDeserializerFor(type); -// EXPECT_TRUE(deserializer); -// EXPECT_EQ(deserializer->deserialize(value), expected); -// } -// -// TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) -// { -// auto deserializers = makeDefaultDeserializersMap(); -// -// auto deserializer = deserializers.getDeserializerFor("json"); -// EXPECT_EQ((deserializer->deserialize(std::nullopt)), (sb::cf::JsonValue{})); -// } -// -// TEST_F(DeserializersTest, ShouldNotFoundDeserializer) -// { -// auto deserializers = makeDefaultDeserializersMap(); -// -// deserializers.set("unknown2", nullptr); -// -// EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); -// EXPECT_FALSE(deserializers.getDeserializerFor("unknown")); -// EXPECT_FALSE(deserializers.getDeserializerFor("unknown2")); -// } -// -// static Params> FailDeserializeValues = { -// // Int -// {"int", "123 abcd"}, -// {"int", "123 "}, -// {"int", "123.123"}, -// {"int", " as 123 abcd"}, -// {"int", "abcd"}, -// {"int", " "}, -// {"int", ""}, -// {"int", "\n"}, -// // UInt -// {"uint", "123 abcd"}, -// {"uint", "123 "}, -// {"uint", "-12"}, -// {"uint", "123.123"}, -// {"uint", " as 123 abcd"}, -// {"uint", "abcd"}, -// {"uint", " "}, -// {"uint", ""}, -// {"uint", "\n"}, -// // Bool -// {"bool", "123 abcd"}, -// {"bool", "123.123"}, -// {"bool", " as 123 abcd"}, -// {"bool", "abcd"}, -// {"bool", " "}, -// {"bool", ""}, -// {"bool", "\n"}, -// {"bool", "ttruee"}, -// {"bool", "ffalsee"}, -// {"bool", "false "}, -// {"bool", "false "}, -// {"bool", " false"}, -// // Double -// {"double", "123 abcd"}, -// {"double", "123 "}, -// {"double", "123 "}, -// {"double", " as 123 abcd"}, -// {"double", "abcd"}, -// {"double", " "}, -// {"double", ""}, -// {"double", "\n"}, -// // Json -// {"json", "123 abcd"}, -// {"json", " as 123 abcd"}, -// {"json", "abcd"}, -// {"json", "{\"hello: 123}"}, -// {"json", "{\"hello\": sde123}"}, -// {"json", " "}, -// {"json", ""}, -// {"json", "\n"}, -// }; -// PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) -// { -// const auto &[type, value] = GetParam(); -// auto deserializers = makeDefaultDeserializersMap(); -// -// auto deserializer = deserializers.getDeserializerFor(type); -// -// EXPECT_TRUE(deserializer); -// EXPECT_ANY_THROW(auto result = deserializer->deserialize(value)); -// } +#include +#include +#include +#include +#include +#include + +#include "SevenBit/Conf/Details/Deserializers.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +#include "Utilities/ParamsTest.hpp" + +class DeserializersTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + DeserializersTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap(std::string_view defaultType = "string", + bool throwOnUnknownType = true) +{ + sb::cf::details::ValueDeserializersMap deserializers{defaultType, throwOnUnknownType}; + deserializers.set("string", std::make_unique()); + deserializers.set("bool", std::make_unique()); + deserializers.set("int", std::make_unique()); + deserializers.set("double", std::make_unique()); + deserializers.set("uint", std::make_unique()); + deserializers.set("json", std::make_unique()); + deserializers.set("null", std::make_unique()); + return std::move(deserializers); +} + +static Params, sb::cf::JsonValue> DeserializeData = { + // Int + {"int", "12", std::int64_t{12}}, + {"int", "-12", std::int64_t{-12}}, + {"Int", "-12", std::int64_t{-12}}, + {"INT", "-12", std::int64_t{-12}}, + {"InT", "-12", std::int64_t{-12}}, + {"int", std::nullopt, std::int64_t{0}}, + {"int", "1", std::int64_t{1}}, + {"int", " 1", std::int64_t{1}}, + {"int", "\t\n 1", std::int64_t{1}}, + {"int", "1", std::int64_t{1}}, + {"Int", "1", std::int64_t{1}}, + {"InT", "1", std::int64_t{1}}, + {"INT", "1", std::int64_t{1}}, + {"int", "-1234", std::int64_t{-1234}}, + {"int", "1234", std::int64_t{1234}}, + {"int", "-223372036854775807", std::int64_t{-223372036854775807ll}}, + {"int", "223372036854775807", std::int64_t{223372036854775807ll}}, + + // UInt + {"uint", "12", std::uint64_t{12}}, + {"Uint", "12", std::uint64_t{12}}, + {"UINT", "12", std::uint64_t{12}}, + {"UiNt", "12", std::uint64_t{12}}, + {"uint", "1222", std::uint64_t{1222}}, + {"uint", std::nullopt, std::uint64_t{0}}, + {"uint", "1", std::uint64_t{1}}, + {"uint", " 1", std::uint64_t{1}}, + {"uint", "\t\n 1", std::uint64_t{1}}, + {"uInt", "1", std::uint64_t{1}}, + {"uInT", "1", std::uint64_t{1}}, + {"UINT", "1", std::uint64_t{1}}, + {"uint", "1234", std::uint64_t{1234}}, + {"uint", "223372036854775807", std::uint64_t{223372036854775807ll}}, + {"uint", "17446744073709551615", std::uint64_t{17446744073709551615ull}}, + + // Bool + {"bool", "true", true}, + {"BoOl", "true", true}, + {"bool", "1", true}, + {"bool", "-11", true}, + {"bool", "0", false}, + {"Bool", "true", true}, + {"BOOL", "true", true}, + {"bool", "True", true}, + {"bool", "TrUe", true}, + {"bool", " 1", true}, + {"bool", "\t\n 1", true}, + {"bool", "-1", true}, + {"bool", "-1123", true}, + {"bool", "1123", true}, + {"bool", "false", false}, + {"bool", "False", false}, + {"bool", "FAlSe", false}, + {"bool", std::nullopt, false}, + // String + {"string", "hello", std::string{"hello"}}, + {"String", "hello", std::string{"hello"}}, + {"STRING", "hello", std::string{"hello"}}, + {"STRING", "hello", std::string{"hello"}}, + {"String", "hello", std::string{"hello"}}, + {"StRinG", "hell\to", std::string{"hell\to"}}, + {"string", "hello", std::string{"hello"}}, + {"string", "", std::string{""}}, + {"string", std::nullopt, std::string{""}}, + // Double + {"double", "1.1", 1.1}, + {"Double", "1.1", 1.1}, + {"DOUBLE", "1.1", 1.1}, + {"DoUBle", "1.1", 1.1}, + {"double", "-11.1", -11.1}, + {"double", "1", 1.0}, + {"double", " 1", 1.0}, + {"double", "\t\n 1", 1.0}, + {"Double", "1", 1.0}, + {"dOuBle", "1", 1.0}, + {"DOUBLE", "1", 1.0}, + {"double", "1.123", 1.123}, + {"double", "-12.21", -12.21}, + {"double", "10000.11", 10000.11}, + {"double", "-10000.11", -10000.11}, + {"double", std::nullopt, 0.0}, + // Null + {"null", "hello", sb::cf::json::null}, + {"Null", "hello", sb::cf::json::null}, + {"NULL", "hello", sb::cf::json::null}, + {"nULl", "hello", sb::cf::json::null}, + {"null", "1", sb::cf::json::null}, + {"null", "asdqwdwq", sb::cf::json::null}, + {"Null", "1", sb::cf::json::null}, + {"NULL", "1asdqwdwq", sb::cf::json::null}, + {"NuLl", "1.123 asdqwdwq", sb::cf::json::null}, + {"null", std::nullopt, sb::cf::json::null}, + // Json + {"json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"Json", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"JSON", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"jSOn", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"JSon", R"({"json": "value"})", sb::cf::JsonObject{{"json", "value"}}}, + {"json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"Json", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"JsOn", R"({"hello": 123456})", sb::cf::JsonObject{{"hello", 123456}}}, + {"json", R"({"hello": [1]})", sb::cf::JsonObject{{"hello", sb::cf::JsonArray{1}}}}, + {"json", R"({"hello": null})", sb::cf::JsonObject{{"hello", sb::cf::json::null}}}, + // Unknown + {"Unknown", "hello", std::string{"hello"}}, + {"sad", "hello", std::string{"hello"}}, + {"ddd", "hello", std::string{"hello"}}, + {"as", "hello", std::string{"hello"}}, + {"qwed", "hello", std::string{"hello"}}, + {"few", "hell\to", std::string{"hell\to"}}, + {"dda", "hello", std::string{"hello"}}, + {"qwdv", "", std::string{""}}, + {"das", std::nullopt, std::string{""}}, +}; +PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) +{ + const auto &[type, value, expected] = GetParam(); + auto deserializers = makeDefaultDeserializersMap("string", false); + + auto &deserializer = deserializers.getDeserializerFor(type); + EXPECT_EQ(deserializer.deserialize(value), expected); +} + +TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) +{ + auto deserializers = makeDefaultDeserializersMap(); + + auto &deserializer = deserializers.getDeserializerFor("json"); + EXPECT_EQ((deserializer.deserialize(std::nullopt)), (sb::cf::JsonValue{})); +} + +TEST_F(DeserializersTest, ShouldNotFoundDeserializer) +{ + auto deserializers = makeDefaultDeserializersMap(); + + deserializers.set("unknown2", nullptr); + + auto get = [&](std::string_view type) { auto &_ = deserializers.getDeserializerFor(type); }; + EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); + EXPECT_THROW(get("unknown"), sb::cf::ConfigException); + EXPECT_THROW(get("unknown2"), sb::cf::ConfigException); +} + +static Params> FailDeserializeValues = { + // Int + {"int", "123 abcd"}, + {"int", "123 "}, + {"int", "123.123"}, + {"int", " as 123 abcd"}, + {"int", "abcd"}, + {"int", " "}, + {"int", ""}, + {"int", "\n"}, + // UInt + {"uint", "123 abcd"}, + {"uint", "123 "}, + {"uint", "-12"}, + {"uint", "123.123"}, + {"uint", " as 123 abcd"}, + {"uint", "abcd"}, + {"uint", " "}, + {"uint", ""}, + {"uint", "\n"}, + // Bool + {"bool", "123 abcd"}, + {"bool", "123.123"}, + {"bool", " as 123 abcd"}, + {"bool", "abcd"}, + {"bool", " "}, + {"bool", ""}, + {"bool", "\n"}, + {"bool", "ttruee"}, + {"bool", "ffalsee"}, + {"bool", "false "}, + {"bool", "false "}, + {"bool", " false"}, + // Double + {"double", "123 abcd"}, + {"double", "123 "}, + {"double", "123 "}, + {"double", " as 123 abcd"}, + {"double", "abcd"}, + {"double", " "}, + {"double", ""}, + {"double", "\n"}, + // Json + {"json", "123 abcd"}, + {"json", " as 123 abcd"}, + {"json", "abcd"}, + {"json", "{\"hello: 123}"}, + {"json", "{\"hello\": sde123}"}, + {"json", " "}, + {"json", ""}, + {"json", "\n"}, +}; +PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) +{ + const auto &[type, value] = GetParam(); + auto deserializers = makeDefaultDeserializersMap(); + + auto &deserializer = deserializers.getDeserializerFor(type); + + EXPECT_ANY_THROW(auto result = deserializer.deserialize(value)); +} diff --git a/Tests/Unit/SettingParserTest.cpp b/Tests/Unit/SettingParserTest.cpp index 61f6496..390dc96 100644 --- a/Tests/Unit/SettingParserTest.cpp +++ b/Tests/Unit/SettingParserTest.cpp @@ -8,12 +8,12 @@ // #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" // #include "SevenBit/Conf/Exceptions.hpp" // -// class SettingParserTest : public testing::Test +// class EnvironmentVarsParserTest : public testing::Test // { // protected: // static void TearUpTestSuite() {} // -// SettingParserTest() {} +// EnvironmentVarsParserTest() {} // // void SetUp() override {} // @@ -22,48 +22,43 @@ // static void TearDownTestSuite() {} // }; // -// TEST_F(SettingParserTest, ShouldParseSetting) +// TEST_F(EnvironmentVarsParserTest, ShouldParseSetting) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); // auto splitter = std::make_unique(); // -// std::string_view setting = "--option:deep:deep!int=123"; +// std::vector settings = {"--option:deep:deep!int=123"}; // sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; // EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); // EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); // sb::cf::JsonValue returnedValue = 123; // EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers)}; // -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_FALSE(parser.getAllowEmptyKeys()); +// EXPECT_EQ(parser.parse(settings), (sb::cf::JsonObject{{"option", {{"deep", {{"deep", 123}}}}}})); // } // -// TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullSplitter) +// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullSplitter) // { // sb::cf::ISettingSplitter::Ptr splitter; // auto deserializers = std::make_unique(); // -// EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, -// true}), +// EXPECT_THROW((sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers)}), // sb::cf::ConfigException); // } // -// TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueNullDeserializers) +// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullDeserializers) // { // sb::cf::IValueDeserializersMap::Ptr deserializers; // auto splitter = std::make_unique(); // -// EXPECT_THROW((sb::cf::details::SettingParser{std::move(splitter), std::move(deserializers), "string", false, -// true}), +// EXPECT_THROW((sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers)}), // sb::cf::ConfigException); // } // -// TEST_F(SettingParserTest, ShouldUseDefaultType) +// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultType) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); @@ -76,13 +71,14 @@ // sb::cf::JsonValue returnedValue = "value"; // EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, +// true}; // // EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); // EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); // } // -// TEST_F(SettingParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) +// TEST_F(EnvironmentVarsParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); @@ -95,7 +91,8 @@ // sb::cf::JsonValue returnedValue = "value"; // EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// true}; // // EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); // EXPECT_EQ(parser.getDefaultType(), "string"); @@ -103,7 +100,7 @@ // EXPECT_TRUE(parser.getAllowEmptyKeys()); // } // -// TEST_F(SettingParserTest, ShouldFailCreateSettingParserDueEmptyKey) +// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueEmptyKey) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); @@ -113,7 +110,8 @@ // sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; // EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", false, true}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, +// true}; // // EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); // EXPECT_EQ(parser.getDefaultType(), "string"); @@ -121,7 +119,7 @@ // EXPECT_FALSE(parser.getAllowEmptyKeys()); // } // -// TEST_F(SettingParserTest, ShouldUseDefaultTypeForUnknownType) +// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultTypeForUnknownType) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); @@ -136,7 +134,8 @@ // sb::cf::JsonValue returnedValue = "value"; // EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// false}; // // EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); // EXPECT_EQ(parser.getDefaultType(), "string"); @@ -144,7 +143,7 @@ // EXPECT_TRUE(parser.getAllowEmptyKeys()); // } // -// TEST_F(SettingParserTest, ShouldFailDueToForUnknownType) +// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownType) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); @@ -155,7 +154,8 @@ // EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); // EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, true}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// true}; // // EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); // EXPECT_EQ(parser.getDefaultType(), "string"); @@ -163,7 +163,7 @@ // EXPECT_TRUE(parser.getAllowEmptyKeys()); // } // -// TEST_F(SettingParserTest, ShouldFailDueToForUnknownDefaultType) +// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownDefaultType) // { // DeserializerMock deserializer; // auto deserializers = std::make_unique(); @@ -175,7 +175,8 @@ // EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); // sb::cf::JsonValue returnedValue = "value"; // -// sb::cf::details::SettingParser parser{std::move(splitter), std::move(deserializers), "string", true, false}; +// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, +// false}; // // EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); // EXPECT_EQ(parser.getDefaultType(), "string"); diff --git a/Tests/Unit/SettingSplitterTest.cpp b/Tests/Unit/SettingSplitterTest.cpp index a1d6571..83f3c90 100644 --- a/Tests/Unit/SettingSplitterTest.cpp +++ b/Tests/Unit/SettingSplitterTest.cpp @@ -1,114 +1,124 @@ -// #include -// #include -// -// #include "SevenBit/Conf/Details/SettingSplitter.hpp" -// #include "SevenBit/Conf/Details/StringUtils.hpp" -// #include "Utilities/ParamsTest.hpp" -// -// -// class SettingSplitterTest : public testing::Test -// { -// protected: -// static void TearUpTestSuite() {} -// -// SettingSplitterTest() {} -// -// void SetUp() override {} -// -// void TearDown() override {} -// -// static void TearDownTestSuite() {} -// }; -// -// static Params SplitSettingData = { -// {"--", {{""}}}, -// {"one", {{"one"}}}, -// {"--====", {{""}, std::nullopt, "==="}}, -// {"--option!!type=val", {{"option!"}, "type", "val"}}, -// {"--option!type=value", {{"option"}, "type", "value"}}, -// {"//option___type;value", {{"option"}, "type", "value"}}, -// {"--option=value", {{"option"}, std::nullopt, "value"}}, -// {"--option=", {{"option"}, std::nullopt, ""}}, -// {"--option!!type=", {{"option!"}, "type", ""}}, -// {"--option!!type", {{"option!"}, "type"}}, -// {"--option!!=type=val", {{"option!"}, "", "type=val"}}, -// {"--option!!:inner!=type=val", {{"option!!", "inner"}, "", "type=val"}}, -// {"--option!!:inner:::!=type=val", {{"option!!", "inner", "", "", ""}, "", "type=val"}}, -// {":option:inner=type=val", {{"", "option", "inner"}, std::nullopt, "type=val"}}, -// {"!!option:inner=type=val", {{"!"}, "option:inner", "type=val"}}, -// {":option:inner=value", {{"", "option", "inner"}, std::nullopt, "value"}}, -// {":option:inner=value::!", {{"", "option", "inner"}, std::nullopt, "value::!"}}, -// {":option!!:inner=value::!", {{"", "option!"}, ":inner", "value::!"}}, -// {":::!=value::!", {{"", "", "", ""}, "", "value::!"}}, -// {"::!:=value::!", {{"", "", ""}, ":", "value::!"}}, -// {":=:!:=value::!", {{"", ""}, std::nullopt, ":!:=value::!"}}, -// {"__=:!___=value::!", {{"", ""}, std::nullopt, ":!___=value::!"}}, -// {"_______=value", {{"", "", ""}, "", "value"}}, -// {"__hello_____type=value", {{"", "hello", ""}, "type", "value"}}, -// {"__hello__type=value", {{"", "hello", "type"}, std::nullopt, "value"}}, -// }; -// PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) -// { -// const auto &[setting, expected] = GetParam(); -// sb::cf::details::SettingSplitter splitter{{"--", "//"}, {"=", ";"}, {"!", "___"}, {":", "__"}}; -// -// EXPECT_EQ(splitter.split(setting), expected); -// } -// -// static OneParams SettingSplittersData = {"=", ">", "===", "<<<<", "////"}; -// -// static OneParams SettingPrefixesData = {":", ";", "%%$", "***", "++"}; -// -// static OneParams KeySplittersData = {":", ";", "__", "{}", "\\"}; -// -// static OneParams TypeMarkersData = {"!", "@", ":", "[]", "{}"}; -// -// static Params, std::string, std::string> SplitSettingSimpleData = { -// {{"key1", "key2", "key3"}, "string", "value"}, -// {{"key1"}, "string", "value"}, -// {{""}, "", ""}, -// {{""}, "type", ""}, -// {{"key"}, "", ""}, -// {{""}, "", "value"}, -// }; -// PARAMS_TEST_COMBINED_5(SettingSplitterTest, ShouldSplitWithDifferentSplitters, SettingPrefixesData, -// SettingSplittersData, TypeMarkersData, KeySplittersData, SplitSettingSimpleData) -// { -// const auto &[prefix, settingSplitter, typeMarker, keySplitter, setting] = GetParam(); -// const auto &[keys, type, value] = setting; -// -// sb::cf::details::SettingSplitter splitter{{prefix}, {settingSplitter}, {typeMarker}, {keySplitter}}; -// -// auto key = sb::cf::details::utils::joinViews(keys, keySplitter); -// auto fullSetting = prefix + key + typeMarker + type + settingSplitter + value; -// EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); -// } -// -// static Params, std::vector, std::vector, -// std::vector, sb::cf::ISettingSplitter::Result> -// EmptySplittersData = { -// {{}, {"=", ";"}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int", "value"}}, -// {{"--", "//"}, {}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, -// {{"--", "//"}, {"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, -// {{"--", "//"}, {"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, -// {{}, {}, {"!", "___"}, {":", "__"}, {{"--key", "deep"}, "int=value"}}, -// {{}, {"=", ";"}, {}, {":", "__"}, {{"--key", "deep!int"}, std::nullopt, "value"}}, -// {{}, {"=", ";"}, {"!", "___"}, {}, {{"--key:deep"}, "int", "value"}}, -// {{"--", "//"}, {}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, -// {{"--", "//"}, {}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, -// {{"--", "//"}, {"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, -// {{}, {}, {}, {":", "__"}, {{"--key", "deep!int=value"}}}, -// {{}, {}, {"!", "___"}, {}, {{"--key:deep"}, "int=value"}}, -// {{}, {"=", ";"}, {}, {}, {{"--key:deep!int"}, std::nullopt, "value"}}, -// {{"--", "//"}, {}, {}, {}, {{"key:deep!int=value"}}}, -// {{}, {}, {}, {}, {{"--key:deep!int=value"}}}, -// -// }; -// PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersData) -// { -// const auto &[prefixes, settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); -// -// sb::cf::details::SettingSplitter splitter{prefixes, settingSplitters, typeMarkers, keySplitters}; -// -// EXPECT_EQ(splitter.split("--key:deep!int=value"), expected); -// } +#include +#include + +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/StringUtils.hpp" +#include "Utilities/ParamsTest.hpp" + +class SettingSplitterTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + SettingSplitterTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +static Params SplitSettingData = { + {"--", {{"--"}}}, + {"one", {{"one"}}}, + {"====", {{""}, std::nullopt, "==="}}, + {"option!!type=val", {{"option!"}, "type", "val"}}, + {"option!type=value", {{"option"}, "type", "value"}}, + {"option___type;value", {{"option"}, "type", "value"}}, + {"option=value", {{"option"}, std::nullopt, "value"}}, + {"option=", {{"option"}, std::nullopt, ""}}, + {"option!!type=", {{"option!"}, "type", ""}}, + {"option!!type", {{"option!"}, "type"}}, + {"option!!=type=val", {{"option!"}, "", "type=val"}}, + {"option!!:inner!=type=val", {{"option!!", "inner"}, "", "type=val"}}, + {"option!!:inner:::!=type=val", {{"option!!", "inner", "", "", ""}, "", "type=val"}}, + {":option:inner=type=val", {{"", "option", "inner"}, std::nullopt, "type=val"}}, + {"!!option:inner=type=val", {{"!"}, "option:inner", "type=val"}}, + {":option:inner=value", {{"", "option", "inner"}, std::nullopt, "value"}}, + {":option:inner=value::!", {{"", "option", "inner"}, std::nullopt, "value::!"}}, + {":option!!:inner=value::!", {{"", "option!"}, ":inner", "value::!"}}, + {":::!=value::!", {{"", "", "", ""}, "", "value::!"}}, + {"::!:=value::!", {{"", "", ""}, ":", "value::!"}}, + {":=:!:=value::!", {{"", ""}, std::nullopt, ":!:=value::!"}}, + {"__=:!___=value::!", {{"", ""}, std::nullopt, ":!___=value::!"}}, + {"_______=value", {{"", "", ""}, "", "value"}}, + {"__hello_____type=value", {{"", "hello", ""}, "type", "value"}}, + {"__hello__type=value", {{"", "hello", "type"}, std::nullopt, "value"}}, +}; +PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) +{ + const auto &[setting, expected] = GetParam(); + sb::cf::details::SettingSplitter splitter{{"=", ";"}, {"!", "___"}, {":", "__"}, true}; + + EXPECT_EQ(splitter.split(setting), expected); +} + +static OneParams SettingSplittersData = {"=", ">", "===", "<<<<", "////"}; + +static OneParams KeySplittersData = {":", ";", "__", "{}", "\\"}; + +static OneParams TypeMarkersData = {"!", "@", ":", "[]", "{}"}; + +static Params, std::string, std::string> SplitSettingSimpleData = { + {{"key1", "key2", "key3"}, "string", "value"}, + {{"key1"}, "string", "value"}, + {{""}, "", ""}, + {{""}, "type", ""}, + {{"key"}, "", ""}, + {{""}, "", "value"}, +}; +PARAMS_TEST_COMBINED_4(SettingSplitterTest, ShouldSplitWithDifferentSplitters, SettingSplittersData, TypeMarkersData, + KeySplittersData, SplitSettingSimpleData) +{ + const auto &[settingSplitter, typeMarker, keySplitter, setting] = GetParam(); + const auto &[keys, type, value] = setting; + + sb::cf::details::SettingSplitter splitter{{settingSplitter}, {typeMarker}, {keySplitter}, true}; + + auto key = sb::cf::details::StringUtils::join(keys, keySplitter); + auto fullSetting = key + typeMarker + type + settingSplitter + value; + EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); +} + +PARAMS_TEST_COMBINED_3(SettingSplitterTest, ShouldFailOnEmptyKeys, SettingSplittersData, TypeMarkersData, + KeySplittersData) +{ + const auto &[settingSplitter, typeMarker, keySplitter] = GetParam(); + + sb::cf::details::SettingSplitter splitter{{settingSplitter}, {typeMarker}, {keySplitter}, false}; + + auto key = sb::cf::details::StringUtils::join({"", "", ""}, keySplitter); + auto fullSetting = key + typeMarker + "type" + settingSplitter + "value"; + auto act = [&] { auto _ = splitter.split(fullSetting); }; + EXPECT_THROW(act(), sb::cf::ConfigException); +} + +static Params, std::vector, std::vector, + sb::cf::ISettingSplitter::Result> + EmptySplittersData = { + {{"=", ";"}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int", "value"}}, + {{}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, + {{"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, + {{"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, + {{}, {"!", "___"}, {":", "__"}, {{"key", "deep"}, "int=value"}}, + {{"=", ";"}, {}, {":", "__"}, {{"key", "deep!int"}, std::nullopt, "value"}}, + {{"=", ";"}, {"!", "___"}, {}, {{"key:deep"}, "int", "value"}}, + {{}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, + {{}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, + {{"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, + {{}, {}, {":", "__"}, {{"key", "deep!int=value"}}}, + {{}, {"!", "___"}, {}, {{"key:deep"}, "int=value"}}, + {{"=", ";"}, {}, {}, {{"key:deep!int"}, std::nullopt, "value"}}, + {{}, {}, {}, {{"key:deep!int=value"}}}, + {{}, {}, {}, {{"key:deep!int=value"}}}, + +}; +PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersData) +{ + const auto &[settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); + + sb::cf::details::SettingSplitter splitter{settingSplitters, typeMarkers, keySplitters, true}; + + EXPECT_EQ(splitter.split("key:deep!int=value"), expected); +} From 2ef936f772b4759ccd39353ad741b8a2fd140835 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 18:32:46 +0100 Subject: [PATCH 08/31] add more tests --- .../Conf/Details/CommandLineParser.hpp | 8 + .../SevenBit/Conf/Details/Configuration.hpp | 10 +- .../Conf/Details/EnvironmentVarsParser.hpp | 4 + .../Conf/Details/Impl/CommandLineParser.hpp | 11 + .../Conf/Details/Impl/Configuration.hpp | 24 ++- .../Details/Impl/EnvironmentVarsParser.hpp | 12 +- .../Conf/Details/Impl/JsonObjectExt.hpp | 79 +++++++- .../Conf/Details/Impl/SettingSplitter.hpp | 19 +- .../Details/Impl/ValueDeserializersMap.hpp | 20 +- Include/SevenBit/Conf/Details/JsonExt.hpp | 14 +- .../SevenBit/Conf/Details/SettingSplitter.hpp | 10 +- .../Conf/Details/ValueDeserializersMap.hpp | 8 +- .../SevenBit/Conf/IConfigurationProvider.hpp | 2 - Include/SevenBit/Conf/ObjectHolder.hpp | 30 +-- .../Sources/ConfigurationProviderBase.hpp | 10 +- .../Sources/EnvironmentVarsConfiguration.hpp | 5 +- .../Sources/Impl/ChainedConfiguration.hpp | 2 +- .../Sources/Impl/CommandLineConfiguration.hpp | 1 - .../Impl/ConfigurationProviderBase.hpp | 24 ++- .../Impl/EnvironmentVarsConfiguration.hpp | 8 +- .../Sources/Impl/InMemoryConfiguration.hpp | 6 +- .../Conf/Sources/Impl/JsonConfiguration.hpp | 6 +- .../Sources/Impl/KeyPerFileConfiguration.hpp | 4 +- .../Conf/Sources/Impl/MapConfiguration.hpp | 13 +- .../Conf/Sources/InMemoryConfiguration.hpp | 4 +- .../Conf/Sources/MapConfiguration.hpp | 8 +- Tests/EndToEnd/CMakeLists.txt | 11 +- Tests/EndToEnd/Cli/testData.json | 39 ---- Tests/EndToEnd/{Cli => }/echo.cpp | 3 +- Tests/EndToEnd/echoTestData.json | 87 ++++++++ Tests/EndToEnd/{Cli/test.py => echo_test.py} | 38 ++-- .../Mocks/ValueDeserializersMapMock.hpp | 3 +- Tests/Integration/CMakeLists.txt | 10 +- .../ConfigurationTest.cpp | 6 +- Tests/Unit/CMakeLists.txt | 9 - Tests/Unit/CommandLineConfigurationTest.cpp | 93 --------- Tests/Unit/CommandLineParserBuilderTest.cpp | 133 ++++++++++++ Tests/Unit/CommandLineParserTest.cpp | 191 ------------------ Tests/Unit/Details/CommandLineParserTest.cpp | 98 +++++++++ .../Unit/{ => Details}/DeserializersTest.cpp | 6 +- .../Details/EnvironmentVarsParserTest.cpp | 59 ++++++ .../Unit/{ => Details}/JsonObjectExtTest.cpp | 6 +- .../{ => Details}/SettingSplitterTest.cpp | 6 +- Tests/Unit/{ => Details}/UtilsTest.cpp | 4 +- .../Unit/EnvironmentVarsParserBuilderTest.cpp | 128 ++++++++++++ Tests/Unit/ObjectHolderTest.cpp | 33 +++ Tests/Unit/SettingParserBuilderTest.cpp | 108 ---------- Tests/Unit/SettingParserTest.cpp | 185 ----------------- .../AppSettingsConfigurationTest.cpp | 0 .../ChainedConfigurationTest.cpp | 0 .../Sources/CommandLineConfigurationTest.cpp | 123 +++++++++++ .../EnvironmentVarsConfigurationTest.cpp | 19 +- .../InMemoryConfigurationTest.cpp | 0 .../{ => Sources}/JsonConfigurationTest.cpp | 0 .../JsonFileConfigurationTest.cpp | 0 .../JsonStreamConfigurationTest.cpp | 0 .../KeyPerFileConfigurationTest.cpp | 0 .../{ => Sources}/MapConfigutationTest.cpp | 0 58 files changed, 971 insertions(+), 769 deletions(-) delete mode 100644 Tests/EndToEnd/Cli/testData.json rename Tests/EndToEnd/{Cli => }/echo.cpp (73%) create mode 100644 Tests/EndToEnd/echoTestData.json rename Tests/EndToEnd/{Cli/test.py => echo_test.py} (58%) rename Tests/{Unit => Integration}/ConfigurationTest.cpp (93%) delete mode 100644 Tests/Unit/CommandLineConfigurationTest.cpp create mode 100644 Tests/Unit/CommandLineParserBuilderTest.cpp delete mode 100644 Tests/Unit/CommandLineParserTest.cpp create mode 100644 Tests/Unit/Details/CommandLineParserTest.cpp rename Tests/Unit/{ => Details}/DeserializersTest.cpp (97%) create mode 100644 Tests/Unit/Details/EnvironmentVarsParserTest.cpp rename Tests/Unit/{ => Details}/JsonObjectExtTest.cpp (98%) rename Tests/Unit/{ => Details}/SettingSplitterTest.cpp (96%) rename Tests/Unit/{ => Details}/UtilsTest.cpp (99%) create mode 100644 Tests/Unit/EnvironmentVarsParserBuilderTest.cpp create mode 100644 Tests/Unit/ObjectHolderTest.cpp delete mode 100644 Tests/Unit/SettingParserBuilderTest.cpp delete mode 100644 Tests/Unit/SettingParserTest.cpp rename Tests/Unit/{ => Sources}/AppSettingsConfigurationTest.cpp (100%) rename Tests/Unit/{ => Sources}/ChainedConfigurationTest.cpp (100%) create mode 100644 Tests/Unit/Sources/CommandLineConfigurationTest.cpp rename Tests/Unit/{ => Sources}/EnvironmentVarsConfigurationTest.cpp (83%) rename Tests/Unit/{ => Sources}/InMemoryConfigurationTest.cpp (100%) rename Tests/Unit/{ => Sources}/JsonConfigurationTest.cpp (100%) rename Tests/Unit/{ => Sources}/JsonFileConfigurationTest.cpp (100%) rename Tests/Unit/{ => Sources}/JsonStreamConfigurationTest.cpp (100%) rename Tests/Unit/{ => Sources}/KeyPerFileConfigurationTest.cpp (100%) rename Tests/Unit/{ => Sources}/MapConfigutationTest.cpp (100%) diff --git a/Include/SevenBit/Conf/Details/CommandLineParser.hpp b/Include/SevenBit/Conf/Details/CommandLineParser.hpp index 80d3a43..9d1b28e 100644 --- a/Include/SevenBit/Conf/Details/CommandLineParser.hpp +++ b/Include/SevenBit/Conf/Details/CommandLineParser.hpp @@ -33,6 +33,14 @@ namespace sb::cf::details [[nodiscard]] JsonObject parse(const std::vector &arguments) const override; + [[nodiscard]] const ISettingSplitter &getOptionsSplitter() const; + + [[nodiscard]] const IValueDeserializersMap &getValueDeserializersMap() const; + + [[nodiscard]] const std::vector &getOptionPrefixes() const; + + [[nodiscard]] bool getConsiderSeparated() const; + private: [[nodiscard]] ArgumentParseResult parseArgument(const std::vector &arguments, size_t &index) const; diff --git a/Include/SevenBit/Conf/Details/Configuration.hpp b/Include/SevenBit/Conf/Details/Configuration.hpp index b28bb0c..aa320e4 100644 --- a/Include/SevenBit/Conf/Details/Configuration.hpp +++ b/Include/SevenBit/Conf/Details/Configuration.hpp @@ -31,13 +31,15 @@ namespace sb::cf::details Configuration &operator=(Configuration &&) = delete; Configuration &operator=(const Configuration &) = delete; - void reload(); + void load(); + + void buildConfig(bool withLoad = false); [[nodiscard]] const std::vector &getProviders() const; [[nodiscard]] std::vector &getProviders(); - [[nodiscard]] std::string toString(std::size_t indent = 1, std::string newLineMark = "\n") const override; + [[nodiscard]] std::string toString(std::size_t indent, std::string newLineMark) const override; [[nodiscard]] JsonValue &root(); @@ -104,9 +106,9 @@ namespace sb::cf::details [[nodiscard]] auto crend() const { return rootAsObject().crend(); } private: - [[nodiscard]] JsonValue &throwNotFoundException(const std::vector &key) const; + [[nodiscard]] static JsonValue &throwNotFoundException(const std::vector &key); - [[nodiscard]] JsonValue &throwNotFoundException(std::string_view key) const; + [[nodiscard]] static JsonValue &throwNotFoundException(std::string_view key); }; } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp index 6c47ce3..94b608e 100644 --- a/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp +++ b/Include/SevenBit/Conf/Details/EnvironmentVarsParser.hpp @@ -23,6 +23,10 @@ namespace sb::cf::details EnvironmentVarsParser(ISettingSplitter::Ptr settingSplitter, IValueDeserializersMap::Ptr valueDeserializersMap); [[nodiscard]] JsonObject parse(const std::vector &envVariables) const override; + + [[nodiscard]] const ISettingSplitter &getSettingSplitter() const; + + [[nodiscard]] const IValueDeserializersMap &getValueDeserializersMap() const; }; } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp b/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp index 549e9fc..6eec0da 100644 --- a/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp +++ b/Include/SevenBit/Conf/Details/Impl/CommandLineParser.hpp @@ -41,6 +41,17 @@ namespace sb::cf::details return result; } + INLINE const ISettingSplitter &CommandLineParser::getOptionsSplitter() const { return *_optionSplitter; } + + INLINE const IValueDeserializersMap &CommandLineParser::getValueDeserializersMap() const + { + return *_valueDeserializersMap; + } + + INLINE const std::vector &CommandLineParser::getOptionPrefixes() const { return _optionPrefixes; } + + INLINE bool CommandLineParser::getConsiderSeparated() const { return _considerSeparated; } + INLINE CommandLineParser::ArgumentParseResult CommandLineParser::parseArgument( const std::vector &arguments, size_t &index) const { diff --git a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp index b609f61..bf31048 100644 --- a/Include/SevenBit/Conf/Details/Impl/Configuration.hpp +++ b/Include/SevenBit/Conf/Details/Impl/Configuration.hpp @@ -14,7 +14,7 @@ namespace sb::cf::details INLINE Configuration::Configuration(std::vector providers) : _providers(std::move(providers)) { - reload(); + buildConfig(true); } INLINE std::string Configuration::toString(std::size_t indent, std::string newLineMark) const @@ -96,15 +96,27 @@ namespace sb::cf::details return deepAt(key); } - INLINE void Configuration::reload() + INLINE void Configuration::load() + { + for (auto &provider : _providers) + { + Require::notNull(provider); + provider->load(); + } + } + + INLINE void Configuration::buildConfig(const bool withLoad) { auto &configRoot = rootAsObject(); configRoot.clear(); for (auto &provider : _providers) { Require::notNull(provider); - provider->load(); - JsonExt::deepMerge(configRoot, std::move(provider->getConfiguration())); + if (withLoad) + { + provider->load(); + } + JsonExt::deepMerge(configRoot, provider->getConfiguration()); } } @@ -112,12 +124,12 @@ namespace sb::cf::details INLINE std::vector &Configuration::getProviders() { return _providers; } - INLINE JsonValue &Configuration::throwNotFoundException(const std::vector &key) const + INLINE JsonValue &Configuration::throwNotFoundException(const std::vector &key) { throw ValueNotFoundException{"Value was not found for key: " + StringUtils::join(key, ":")}; } - INLINE JsonValue &Configuration::throwNotFoundException(std::string_view key) const + INLINE JsonValue &Configuration::throwNotFoundException(std::string_view key) { throw ValueNotFoundException{"Value was not found for key: " + std::string{key}}; } diff --git a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp index 1d47bac..0aa59e2 100644 --- a/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp +++ b/Include/SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp @@ -24,8 +24,9 @@ namespace sb::cf::details { try { - auto [keys, type, value] = _settingSplitter->split(variable); - JsonExt::updateWith(result, keys, _valueDeserializersMap->getDeserializerFor(type).deserialize(value)); + auto [keys, type, value] = getSettingSplitter().split(variable); + JsonExt::updateWith(result, keys, + getValueDeserializersMap().getDeserializerFor(type).deserialize(value)); } catch (const std::exception &e) { @@ -35,4 +36,11 @@ namespace sb::cf::details } return result; } + + INLINE const ISettingSplitter &EnvironmentVarsParser::getSettingSplitter() const { return *_settingSplitter; } + + INLINE const IValueDeserializersMap &EnvironmentVarsParser::getValueDeserializersMap() const + { + return *_valueDeserializersMap; + } } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp b/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp index 1450a8e..15fd6d0 100644 --- a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp +++ b/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp @@ -194,6 +194,56 @@ namespace sb::cf::details return *current; } + INLINE void JsonExt::deepMerge(JsonValue &json, const JsonValue &override) + { + if (override.is_uninitialized()) // undefined is mark for skip + { + return; + } + if (json.is_object() && override.is_object()) + { + deepMerge(json.get_object(), override.get_object()); + } + else if (json.is_array() && override.is_array()) + { + deepMerge(json.get_array(), override.get_array()); + } + else + { + json = override; + } + } + + INLINE void JsonExt::deepMerge(JsonArray &json, const JsonArray &override) + { + if (json.empty()) + { + json = override; + return; + } + for (size_t i = 0; i < override.size(); ++i) + { + if (i >= json.size()) + { + json.emplace_back(); + } + deepMerge(json[i], override[i]); + } + } + + INLINE void JsonExt::deepMerge(JsonObject &json, const JsonObject &override) + { + if (json.empty()) + { + json = override; + return; + } + for (auto &[key, value] : override) + { + deepMerge(json[key], value); + } + } + INLINE void JsonExt::deepMerge(JsonValue &json, JsonValue &&override) { if (override.is_uninitialized()) // undefined is mark for skip @@ -244,14 +294,25 @@ namespace sb::cf::details } } - INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value) + INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, const JsonValue &value) { - deepGetOrOverride(json, keys) = std::move(value); + deepGetOrOverride(json, keys) = value; } - INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonObject &&value) + INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, + const JsonObject &value) { - deepGetOrOverride(json, keys) = std::move(value); + deepGetOrOverride(json, keys) = value; + } + + INLINE void JsonExt::updateWith(JsonObject &json, std::string_view key, const JsonValue &value) + { + deepGetOrOverride(json, key) = value; + } + + INLINE void JsonExt::updateWith(JsonObject &json, std::string_view key, const JsonObject &value) + { + deepGetOrOverride(json, key) = value; } INLINE void JsonExt::updateWith(JsonObject &json, std::string_view key, JsonValue &&value) @@ -264,6 +325,16 @@ namespace sb::cf::details deepGetOrOverride(json, key) = std::move(value); } + INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value) + { + deepGetOrOverride(json, keys) = std::move(value); + } + + INLINE void JsonExt::updateWith(JsonObject &json, const std::vector &keys, JsonObject &&value) + { + deepGetOrOverride(json, keys) = std::move(value); + } + INLINE void JsonExt::checkSegmentSize(const std::vector &key) { if (key.empty()) diff --git a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp index d5c261f..5f52d6f 100644 --- a/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/Impl/SettingSplitter.hpp @@ -21,10 +21,21 @@ namespace sb::cf::details return {splitKey(rawKey), type, value}; } + INLINE const std::vector &SettingSplitter::getSettingSplitters() const + { + return _settingSplitters; + } + + INLINE const std::vector &SettingSplitter::getTypeMarkers() const { return _typeMarkers; } + + INLINE const std::vector &SettingSplitter::getKeySplitters() const { return _keySplitters; } + + INLINE bool SettingSplitter::getAllowEmptyKeys() const { return _allowEmptyKeys; } + INLINE std::pair> SettingSplitter::splitSetting( const std::string_view setting) const { - if (auto breakResult = StringUtils::tryBreak(setting, _settingSplitters)) + if (auto breakResult = StringUtils::tryBreak(setting, getSettingSplitters())) { return {breakResult->first, breakResult->second}; } @@ -33,7 +44,7 @@ namespace sb::cf::details INLINE std::optional SettingSplitter::tryExtractType(std::string_view &key) const { - if (auto breakResult = StringUtils::tryBreakFromEnd(key, _typeMarkers)) + if (auto breakResult = StringUtils::tryBreakFromEnd(key, getTypeMarkers())) { key = breakResult->first; return breakResult->second; @@ -43,14 +54,14 @@ namespace sb::cf::details INLINE std::vector SettingSplitter::splitKey(const std::string_view key) const { - auto keys = StringUtils::split(key, _keySplitters); + auto keys = StringUtils::split(key, getKeySplitters()); checkKeys(keys); return keys; } INLINE void SettingSplitter::checkKeys(const std::vector &keys) const { - if (_allowEmptyKeys) + if (getAllowEmptyKeys()) { return; } diff --git a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp index cd5e210..d270e5b 100644 --- a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp @@ -15,11 +15,6 @@ namespace sb::cf::details } } - INLINE ValueDeserializersMap::DeserializersMap &ValueDeserializersMap::getDeserializersMap() - { - return _deserializersMap; - } - INLINE void ValueDeserializersMap::set(const std::string_view type, IDeserializer::Ptr deserializer) { _deserializersMap[std::string{type}] = std::move(deserializer); @@ -32,20 +27,29 @@ namespace sb::cf::details { return *deserializer; } - if (_throwOnUnknownType) + if (getThrowOnUnknownType()) { throw ConfigException("Unknown type: '" + std::string{*type} + "' deserializer for type was not found"); } return getDefaultDeserializer(); } + INLINE const ValueDeserializersMap::DeserializersMap &ValueDeserializersMap::getDeserializersMap() const + { + return _deserializersMap; + } + + INLINE std::string_view ValueDeserializersMap::getDefaultType() const { return _defaultType; } + + INLINE bool ValueDeserializersMap::getThrowOnUnknownType() const { return _throwOnUnknownType; } + INLINE const IDeserializer &ValueDeserializersMap::getDefaultDeserializer() const { - if (const auto deserializer = findDeserializerFor(_defaultType)) + if (const auto deserializer = findDeserializerFor(getDefaultType())) { return *deserializer; } - throw ConfigException("Unknown default type: '" + std::string{_defaultType} + + throw ConfigException("Unknown default type: '" + std::string{getDefaultType()} + "' deserializer for type was not found"); } diff --git a/Include/SevenBit/Conf/Details/JsonExt.hpp b/Include/SevenBit/Conf/Details/JsonExt.hpp index f7dbfa4..83e6b90 100644 --- a/Include/SevenBit/Conf/Details/JsonExt.hpp +++ b/Include/SevenBit/Conf/Details/JsonExt.hpp @@ -49,14 +49,24 @@ namespace sb::cf::details static JsonValue &deepGetOrOverride(JsonObject &json, std::string_view key); static JsonValue &deepGetOrOverride(JsonObject &json, const std::vector &keys); + static void deepMerge(JsonValue &json, const JsonValue &override); + static void deepMerge(JsonArray &json, const JsonArray &override); + static void deepMerge(JsonObject &json, const JsonObject &override); + static void deepMerge(JsonValue &json, JsonValue &&override); static void deepMerge(JsonArray &json, JsonArray &&override); static void deepMerge(JsonObject &json, JsonObject &&override); - static void updateWith(JsonObject &json, std::string_view key, JsonValue &&value); - static void updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value); + static void updateWith(JsonObject &json, std::string_view key, const JsonValue &value); + static void updateWith(JsonObject &json, std::string_view key, const JsonObject &value); + + static void updateWith(JsonObject &json, const std::vector &keys, const JsonValue &value); + static void updateWith(JsonObject &json, const std::vector &keys, const JsonObject &value); + static void updateWith(JsonObject &json, std::string_view key, JsonValue &&value); static void updateWith(JsonObject &json, std::string_view key, JsonObject &&value); + + static void updateWith(JsonObject &json, const std::vector &keys, JsonValue &&value); static void updateWith(JsonObject &json, const std::vector &keys, JsonObject &&value); static void checkSegmentSize(const std::vector &key); diff --git a/Include/SevenBit/Conf/Details/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/SettingSplitter.hpp index 8de3dc2..aa552ff 100644 --- a/Include/SevenBit/Conf/Details/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/SettingSplitter.hpp @@ -18,7 +18,7 @@ namespace sb::cf::details const std::vector _settingSplitters; const std::vector _typeMarkers; const std::vector _keySplitters; - bool _allowEmptyKeys; + const bool _allowEmptyKeys; public: SettingSplitter(std::vector settingSplitters, std::vector typeMarkers, @@ -26,6 +26,14 @@ namespace sb::cf::details [[nodiscard]] Result split(std::string_view setting) const override; + [[nodiscard]] const std::vector &getSettingSplitters() const; + + [[nodiscard]] const std::vector &getTypeMarkers() const; + + [[nodiscard]] const std::vector &getKeySplitters() const; + + [[nodiscard]] bool getAllowEmptyKeys() const; + private: [[nodiscard]] std::pair> splitSetting( std::string_view setting) const; diff --git a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp index 2766ec5..fc615d1 100644 --- a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp @@ -45,12 +45,16 @@ namespace sb::cf::details ValueDeserializersMap &operator=(const ValueDeserializersMap &) = delete; ValueDeserializersMap &operator=(ValueDeserializersMap &&) = delete; - [[nodiscard]] DeserializersMap &getDeserializersMap(); - void set(std::string_view type, IDeserializer::Ptr deserializer); [[nodiscard]] const IDeserializer &getDeserializerFor(std::optional type) const override; + [[nodiscard]] const DeserializersMap &getDeserializersMap() const; + + [[nodiscard]] std::string_view getDefaultType() const; + + [[nodiscard]] bool getThrowOnUnknownType() const; + private: [[nodiscard]] const IDeserializer &getDefaultDeserializer() const; diff --git a/Include/SevenBit/Conf/IConfigurationProvider.hpp b/Include/SevenBit/Conf/IConfigurationProvider.hpp index d070288..83e4ec8 100644 --- a/Include/SevenBit/Conf/IConfigurationProvider.hpp +++ b/Include/SevenBit/Conf/IConfigurationProvider.hpp @@ -16,8 +16,6 @@ namespace sb::cf [[nodiscard]] virtual const JsonObject &getConfiguration() const = 0; - virtual JsonObject &getConfiguration() = 0; - virtual ~IConfigurationProvider() = default; }; } // namespace sb::cf diff --git a/Include/SevenBit/Conf/ObjectHolder.hpp b/Include/SevenBit/Conf/ObjectHolder.hpp index 454d7a7..13df43f 100644 --- a/Include/SevenBit/Conf/ObjectHolder.hpp +++ b/Include/SevenBit/Conf/ObjectHolder.hpp @@ -10,7 +10,6 @@ namespace sb::cf { template class ObjectHolder : public IObject { - private: T _object; explicit ObjectHolder(const T &object) : _object(object) {} @@ -18,36 +17,27 @@ namespace sb::cf explicit ObjectHolder(T &&object) : _object(std::move(object)) {} public: - using Ptr = std::unique_ptr>; + using Ptr = std::unique_ptr; - [[nodiscard]] static ObjectHolder::Ptr from(const T &object) - { - return ObjectHolder::Ptr{new ObjectHolder{object}}; - } + [[nodiscard]] static Ptr from(const T &object) { return Ptr{new ObjectHolder{object}}; } - [[nodiscard]] static ObjectHolder::Ptr from(T &&object) - { - return ObjectHolder::Ptr{new ObjectHolder{object}}; - } + [[nodiscard]] static Ptr from(T &&object) { return Ptr{new ObjectHolder{object}}; } - [[nodiscard]] static ObjectHolder &castFrom(IObject &object) - { - return static_cast &>(object); - } + [[nodiscard]] static ObjectHolder &castFrom(IObject &object) { return static_cast(object); } - [[nodiscard]] static const ObjectHolder &castFrom(const IObject &object) + [[nodiscard]] static const ObjectHolder &castFrom(const IObject &object) { - return static_cast &>(object); + return static_cast(object); } - [[nodiscard]] static ObjectHolder &safeCastFrom(IObject &object) + [[nodiscard]] static ObjectHolder &safeCastFrom(IObject &object) { - return dynamic_cast &>(object); + return dynamic_cast(object); } - [[nodiscard]] static const ObjectHolder &safeCastFrom(const IObject &object) + [[nodiscard]] static const ObjectHolder &safeCastFrom(const IObject &object) { - return dynamic_cast &>(object); + return dynamic_cast(object); } [[nodiscard]] T &get() { return _object; } diff --git a/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp b/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp index 519a0b3..c8b715f 100644 --- a/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp +++ b/Include/SevenBit/Conf/Sources/ConfigurationProviderBase.hpp @@ -18,16 +18,22 @@ namespace sb::cf public: [[nodiscard]] const JsonObject &getConfiguration() const override; - [[nodiscard]] JsonObject &getConfiguration() override; + [[nodiscard]] JsonObject &getConfiguration(); protected: void clear(); + void set(const JsonObject &configuration); void set(JsonObject &&configuration); + void update(const JsonObject &configuration); void update(JsonObject &&configuration); - void update(const std::vector &keys, JsonValue &&value); + void updateWith(std::string_view key, const JsonValue &value); + void updateWith(std::string_view key, JsonValue &&value); + + void updateWith(const std::vector &keys, const JsonValue &value); + void updateWith(const std::vector &keys, JsonValue &&value); }; } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp index d3601b2..d5f6b9b 100644 --- a/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp @@ -25,8 +25,7 @@ namespace sb::cf using Ptr = std::unique_ptr; using SPtr = std::shared_ptr; - [[nodiscard]] static SPtr create(std::string prefix, - ISettingsParser::Ptr parser = EnvironmentVarsParserBuilder{}.build()); + [[nodiscard]] static SPtr create(std::string prefix, ISettingsParser::Ptr parser); [[nodiscard]] const std::string &getPrefix(); @@ -46,7 +45,7 @@ namespace sb::cf void load() override; private: - [[nodiscard]] std::vector getEnvVars(); + [[nodiscard]] std::vector getEnvVars() const; }; } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp index d16f46d..0c1f6c4 100644 --- a/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp @@ -57,7 +57,7 @@ namespace sb::cf { details::Require::notNull(provider); provider->load(); - update(std::move(provider->getConfiguration())); + update(provider->getConfiguration()); } } diff --git a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp index 1cfdc11..2b2af05 100644 --- a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp @@ -53,7 +53,6 @@ namespace sb::cf INLINE void CommandLineConfigurationProvider::load() { - clear(); set(_source->getSettingsParser().parse(_source->getArgs())); } } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp b/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp index 0ea1644..e2be6a1 100644 --- a/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ConfigurationProviderBase.hpp @@ -14,17 +14,39 @@ namespace sb::cf INLINE void ConfigurationProviderBase::clear() { _configuration.clear(); } + INLINE void ConfigurationProviderBase::set(const JsonObject &configuration) { _configuration = configuration; } + INLINE void ConfigurationProviderBase::set(JsonObject &&configuration) { _configuration = std::move(configuration); } + INLINE void ConfigurationProviderBase::update(const JsonObject &configuration) + { + details::JsonExt::deepMerge(_configuration, configuration); + } + INLINE void ConfigurationProviderBase::update(JsonObject &&configuration) { details::JsonExt::deepMerge(_configuration, std::move(configuration)); } - INLINE void ConfigurationProviderBase::update(const std::vector &keys, JsonValue &&value) + INLINE void ConfigurationProviderBase::updateWith(std::string_view key, const JsonValue &value) + { + details::JsonExt::updateWith(_configuration, key, value); + } + + INLINE void ConfigurationProviderBase::updateWith(std::string_view key, JsonValue &&value) + { + details::JsonExt::updateWith(_configuration, key, std::move(value)); + } + + INLINE void ConfigurationProviderBase::updateWith(const std::vector &keys, const JsonValue &value) + { + details::JsonExt::updateWith(_configuration, keys, value); + } + + INLINE void ConfigurationProviderBase::updateWith(const std::vector &keys, JsonValue &&value) { details::JsonExt::updateWith(_configuration, keys, std::move(value)); } diff --git a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp index 18f6c00..f312ad0 100644 --- a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp @@ -46,13 +46,9 @@ namespace sb::cf details::Require::notNull(_source); } - INLINE void EnvironmentVarsConfigurationProvider::load() - { - clear(); - set(_source->getSettingParser().parse(getEnvVars())); - } + INLINE void EnvironmentVarsConfigurationProvider::load() { set(_source->getSettingParser().parse(getEnvVars())); } - INLINE std::vector EnvironmentVarsConfigurationProvider::getEnvVars() + INLINE std::vector EnvironmentVarsConfigurationProvider::getEnvVars() const { std::vector result; auto &prefix = _source->getPrefix(); diff --git a/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp index 24470b9..0155b03 100644 --- a/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/InMemoryConfiguration.hpp @@ -32,11 +32,9 @@ namespace sb::cf INLINE void InMemoryConfigurationProvider::load() { clear(); - for (auto [key, value] : *_source) + for (auto &[key, value] : *_source) { - JsonObject result{}; - details::JsonExt::updateWith(result, key, std::move(value)); - update(std::move(result)); + updateWith(key, value); } } } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp index adf2303..f59bb94 100644 --- a/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/JsonConfiguration.hpp @@ -28,9 +28,5 @@ namespace sb::cf details::Require::notNull(_source); } - INLINE void JsonConfigurationProvider::load() - { - auto copy = _source->getJson(); - set(std::move(copy)); - } + INLINE void JsonConfigurationProvider::load() { set(_source->getJson()); } } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp index f66bc9a..00af0a8 100644 --- a/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp @@ -80,10 +80,10 @@ namespace sb::cf { if (auto fileSource = tryGetFileSource(filePath)) { - auto mapFcn = [name = filePath.stem().generic_string()](JsonObject &&config) -> JsonObject { + auto mapFcn = [name = filePath.stem().generic_string()](const JsonObject &config) -> JsonObject { auto res = JsonObject{}; const auto keys = details::StringUtils::split(name, "__"); - details::JsonExt::updateWith(res, keys, std::move(config)); + details::JsonExt::updateWith(res, keys, config); return res; }; return MapConfigurationSource::create(std::move(fileSource), std::move(mapFcn)); diff --git a/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp index 2080b27..a100490 100644 --- a/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/MapConfiguration.hpp @@ -6,19 +6,22 @@ namespace sb::cf { INLINE MapConfigurationSource::MapConfigurationSource(IConfigurationSource::SPtr source, - std::function mapFcn) + std::function mapFcn) : _source(std::move(source)), _mapFcn(std::move(mapFcn)) { details::Require::notNull(_source); } - INLINE MapConfigurationSource::SPtr MapConfigurationSource::create(IConfigurationSource::SPtr source, - std::function mapFcn) + INLINE MapConfigurationSource::SPtr MapConfigurationSource::create( + IConfigurationSource::SPtr source, std::function mapFcn) { return MapConfigurationSource::SPtr(new MapConfigurationSource{std::move(source), std::move(mapFcn)}); } - INLINE const std::function &MapConfigurationSource::getMapFcn() const { return _mapFcn; } + INLINE const std::function &MapConfigurationSource::getMapFcn() const + { + return _mapFcn; + } INLINE IConfigurationProvider::Ptr MapConfigurationSource::build(IConfigurationBuilder &builder) { @@ -38,6 +41,6 @@ namespace sb::cf details::Require::notNull(_source); details::Require::notNull(_innerProvider); _innerProvider->load(); - set(_source->getMapFcn()(std::move(_innerProvider->getConfiguration()))); + set(_source->getMapFcn()(_innerProvider->getConfiguration())); } } // namespace sb::cf diff --git a/Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp b/Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp index 0b0e732..b933867 100644 --- a/Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/InMemoryConfiguration.hpp @@ -28,9 +28,9 @@ namespace sb::cf IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; - [[nodiscard]] auto begin() { return _settings.begin(); } + [[nodiscard]] auto begin() const { return _settings.begin(); } - [[nodiscard]] auto end() { return _settings.end(); } + [[nodiscard]] auto end() const { return _settings.end(); } }; class EXPORT InMemoryConfigurationProvider : public ConfigurationProviderBase diff --git a/Include/SevenBit/Conf/Sources/MapConfiguration.hpp b/Include/SevenBit/Conf/Sources/MapConfiguration.hpp index 886012b..582949d 100644 --- a/Include/SevenBit/Conf/Sources/MapConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/MapConfiguration.hpp @@ -16,18 +16,18 @@ namespace sb::cf { private: IConfigurationSource::SPtr _source; - std::function _mapFcn; + std::function _mapFcn; - MapConfigurationSource(IConfigurationSource::SPtr source, std::function mapFcn); + MapConfigurationSource(IConfigurationSource::SPtr source, std::function mapFcn); public: using Ptr = std::unique_ptr; using SPtr = std::shared_ptr; [[nodiscard]] static SPtr create(IConfigurationSource::SPtr source, - std::function mapFcn); + std::function mapFcn); - [[nodiscard]] const std::function &getMapFcn() const; + [[nodiscard]] const std::function &getMapFcn() const; IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; }; diff --git a/Tests/EndToEnd/CMakeLists.txt b/Tests/EndToEnd/CMakeLists.txt index 3beb8c3..0a8ccc5 100644 --- a/Tests/EndToEnd/CMakeLists.txt +++ b/Tests/EndToEnd/CMakeLists.txt @@ -1,10 +1,9 @@ -add_executable(CliEcho Cli/echo.cpp) +add_executable(Echo echo.cpp) -target_link_libraries(CliEcho 7bitConf) +target_link_libraries(Echo 7bitConf) find_package(Python REQUIRED) -add_test(NAME EndToEnd.CommandLine - COMMAND ${Python_EXECUTABLE} test.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} - CONFIGURATIONS --exe $ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Cli) +add_test(NAME EndToEnd.Echo + COMMAND ${Python_EXECUTABLE} echo_test.py $ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/EndToEnd/Cli/testData.json b/Tests/EndToEnd/Cli/testData.json deleted file mode 100644 index df6aedb..0000000 --- a/Tests/EndToEnd/Cli/testData.json +++ /dev/null @@ -1,39 +0,0 @@ -[ - [ - [ - "testarg=123" - ], - { - "testarg": "123" - } - ], - [ - [ - "--testarg", - "123" - ], - { - "testarg": "123" - } - ], - [ - [ - "--testarg", - "123", - "/testarg2", - "12", - "testarg3=321", - "testarg4=2", - "testarg5", - "--testarg6" - ], - { - "testarg": "123", - "testarg2": "12", - "testarg3": "321", - "testarg4": "2", - "testarg5": "", - "testarg6": "" - } - ] -] diff --git a/Tests/EndToEnd/Cli/echo.cpp b/Tests/EndToEnd/echo.cpp similarity index 73% rename from Tests/EndToEnd/Cli/echo.cpp rename to Tests/EndToEnd/echo.cpp index f097ecd..68fba0f 100644 --- a/Tests/EndToEnd/Cli/echo.cpp +++ b/Tests/EndToEnd/echo.cpp @@ -1,4 +1,4 @@ -#include +#include <../../Include/SevenBit/Conf.hpp> #include using namespace sb::cf; @@ -7,6 +7,7 @@ int main(int argc, char **argv) { IConfiguration::Ptr configuration = ConfigurationBuilder{} // .addCommandLine(argc, argv) + .addEnvironmentVariables() .build(); std::cout << *configuration; return 0; diff --git a/Tests/EndToEnd/echoTestData.json b/Tests/EndToEnd/echoTestData.json new file mode 100644 index 0000000..30a2a33 --- /dev/null +++ b/Tests/EndToEnd/echoTestData.json @@ -0,0 +1,87 @@ +[ + { + "args": [ + "testarg=123" + ], + "env": {}, + "expected": { + "testarg": "123" + } + }, + { + "args": [ + "--testarg", + "123" + ], + "env": {}, + "expected": { + "testarg": "123" + } + }, + { + "args": [ + "--testarg", + "123", + "/testarg2", + "12", + "testarg3=321", + "testarg4=2", + "testarg5", + "--testarg6" + ], + "env": {}, + "expected": { + "testarg": "123", + "testarg2": "12", + "testarg3": "321", + "testarg4": "2", + "testarg5": "", + "testarg6": "" + } + }, + { + "args": [ + "--str", + "123", + "/number:nested!int", + "12", + "unumber!uint=321", + "flag!bool=true", + "--json!json", + "[1,2,3,4,5,6]", + "double=12.345", + "--null!null" + ], + "env": { + "number2___int": "321", + "json__1___int": "321", + "flag": "false", + "newOption:0:master!string": "mystring", + "number__nested___int": "3333" + }, + "expected": { + "double": "12.345", + "flag": "false", + "json": [ + 1, + 321, + 3, + 4, + 5, + 6 + ], + "newOption": [ + { + "master": "mystring" + } + ], + "null": null, + "number": { + "nested": 3333 + }, + "number2": 321, + "str": "123", + "unumber": 321 + } + } +] diff --git a/Tests/EndToEnd/Cli/test.py b/Tests/EndToEnd/echo_test.py similarity index 58% rename from Tests/EndToEnd/Cli/test.py rename to Tests/EndToEnd/echo_test.py index 0512b76..96fbc43 100644 --- a/Tests/EndToEnd/Cli/test.py +++ b/Tests/EndToEnd/echo_test.py @@ -4,28 +4,26 @@ import sys -def getBinDir(): - return '/Users/sylwesterdawida/7bitConf/cmake-build-debug/bin' +def getEchoExecPath(): if len(sys.argv) != 2: - raise Exception("binary directory not provided") - binDir = sys.argv[1] - if not os.path.exists(binDir): - raise Exception("Binary directory does not exist") - return binDir + raise Exception("Echo executable not provided") + echoExec = sys.argv[1] + if not os.path.exists(echoExec): + raise Exception("Echo executable does not exist") + return echoExec -class CliEchoTest: - def __init__(self, binDir): - self.binDir = binDir - self.echoExecPath = os.path.join(self.binDir, "CliEcho" + ('.exe' if sys.platform == 'win32' else '')) +class EchoTest: + def __init__(self, echoExecPath): + self.echoExecPath = echoExecPath self.testsData = self.__getTestsData() def __getTestsData(self): - with open('testData.json') as data: + with open('echoTestData.json') as data: return json.load(data) - def __runTest(self, args, expectedJson): - result = subprocess.run([self.echoExecPath, *args], capture_output=True, text=True) + def __runTest(self, args, env, expectedJson): + result = subprocess.run([self.echoExecPath, *args], env=env, capture_output=True, text=True) if result.returncode: raise Exception(f"test returned non zero code {result.returncode}") formatedOutput = json.dumps(json.loads(result.stdout)) @@ -34,13 +32,15 @@ def __runTest(self, args, expectedJson): raise Exception(f"result of running test: '{formatedOutput}' does not match expected: '{formatedExpected}'") def __runTestAndSummarize(self, testData): - args, expectedJson = testData + args = testData["args"] + env = testData["env"] + expectedJson = testData["expected"] try: - self.__runTest(args, expectedJson) - print(f"Test for args: {args} succeeded") + self.__runTest(args, env, expectedJson) + print(f"Test for args: {args}, env: {env} succeeded") return True except Exception as e: - print(f"Test for args: {args} failed: {e}") + print(f"Test for args: {args}, env: {env} failed: {e}") return False def run(self): @@ -56,4 +56,4 @@ def run(self): if __name__ == "__main__": - CliEchoTest(getBinDir()).run() + EchoTest(getEchoExecPath()).run() diff --git a/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp b/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp index 1fe4350..4c9f0fe 100644 --- a/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp +++ b/Tests/Helpers/Mocks/ValueDeserializersMapMock.hpp @@ -6,5 +6,6 @@ struct ValueDeserializersMapMock : public sb::cf::IValueDeserializersMap { - MOCK_METHOD((const sb::cf::IDeserializer *), getDeserializerFor, (std::string_view), (const override)); + MOCK_METHOD((const sb::cf::IDeserializer &), getDeserializerFor, (std::optional), + (const override)); }; diff --git a/Tests/Integration/CMakeLists.txt b/Tests/Integration/CMakeLists.txt index 42f8b37..ecfed9a 100644 --- a/Tests/Integration/CMakeLists.txt +++ b/Tests/Integration/CMakeLists.txt @@ -1,15 +1,15 @@ file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS *.cpp) add_executable(IntegrationTests - ${SOURCES} + ${SOURCES} ) target_link_libraries(IntegrationTests PUBLIC - GTest::gtest - GTest::gmock - 7bitDI + GTest::gtest + GTest::gmock + 7bitConf ) include(GoogleTest) gtest_discover_tests(IntegrationTests - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/Tests/Unit/ConfigurationTest.cpp b/Tests/Integration/ConfigurationTest.cpp similarity index 93% rename from Tests/Unit/ConfigurationTest.cpp rename to Tests/Integration/ConfigurationTest.cpp index 3b9989c..bffcc29 100644 --- a/Tests/Unit/ConfigurationTest.cpp +++ b/Tests/Integration/ConfigurationTest.cpp @@ -22,7 +22,7 @@ TEST_F(ConfigurationTest, ShouldLoadConfig) auto conf = sb::cf::ConfigurationBuilder{} .addAppSettings("dev") .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "Array:0!int=33"}) + .addCommandLine({"--string", "2", "Array:0!int=33"}) .AddInMemory("set:set", 44444) .addKeyPerFile("Directory") .build(); @@ -51,7 +51,7 @@ TEST_F(ConfigurationTest, ShouldFindConfgValues) auto conf = sb::cf::ConfigurationBuilder{} .addAppSettings("dev") .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "Array:0!int=33"}) + .addCommandLine({"--string", "2", "Array:0!int=33"}) .AddInMemory("set:set", 44444) .addKeyPerFile("Directory") .build(); @@ -72,7 +72,7 @@ TEST_F(ConfigurationTest, ShouldSerializeValues) { auto conf = sb::cf::ConfigurationBuilder{} .addJson({{"string", 1}}) - .addCommandLine({"--string=2", "Array:0!int=33"}) + .addCommandLine({"--string", "2", "Array:0!int=33"}) .AddInMemory("set:set", 44444) .build(); diff --git a/Tests/Unit/CMakeLists.txt b/Tests/Unit/CMakeLists.txt index dd9c363..c7642aa 100644 --- a/Tests/Unit/CMakeLists.txt +++ b/Tests/Unit/CMakeLists.txt @@ -10,15 +10,6 @@ target_link_libraries(UnitTests PUBLIC 7bitConf ) -file(GLOB_RECURSE FILES CONFIGURE_DEPENDS Files/*) -file(GLOB_RECURSE FILES_DIR CONFIGURE_DEPENDS Files/Directory/*) - -file(COPY ${FILES} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -file(COPY ${FILES_DIR} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Directory) - -file(COPY ${FILES} DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY ${FILES_DIR} DESTINATION ${CMAKE_BINARY_DIR}/Directory) - include(GoogleTest) gtest_discover_tests(UnitTests WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/Tests/Unit/CommandLineConfigurationTest.cpp b/Tests/Unit/CommandLineConfigurationTest.cpp deleted file mode 100644 index 1018939..0000000 --- a/Tests/Unit/CommandLineConfigurationTest.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// #include -// #include -// -// #include "Mocks/ConfigurationBuilderMock.hpp" -// #include "SevenBit/Conf/Exceptions.hpp" -// #include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" -// -// class CommandLineConfigurationTest : public testing::Test -// { -// protected: -// ConfigurationBuilderMock mock; -// -// static void TearUpTestSuite() {} -// -// CommandLineConfigurationTest() {} -// -// void SetUp() override {} -// -// void TearDown() override {} -// -// static void TearDownTestSuite() {} -// }; -// -// TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) -// { -// std::vector args; -// EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), -// sb::cf::NullPointerException); -// } -// -// TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) -// { -// const char *const argv[] = { -// "program/path", "--string___string=test", "--list:0!string=string", "double!double=1.22", -// "true_bool!bool=-1", "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", -// "--list:1=string1", "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", -// "--int_list:1___int=22", "int_list__2!int=11", -// }; -// int size = sizeof(argv) / sizeof(char *); -// auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); -// -// provider->load(); -// -// sb::cf::JsonObject expected = {{"string", "test"}, -// {"double", 1.22}, -// {"false_bool", false}, -// {"false_bool2", false}, -// {"true_bool", true}, -// {"true_bool2", true}, -// {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, -// {"int_list", sb::cf::JsonArray{33, 22, 11}}, -// {"object", {{"inner", {{"object", "string"}}}}}}; -// -// EXPECT_EQ(provider->getConfiguration(), expected); -// } -// -// TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) -// { -// std::vector args = { -// "--string___string=test", "--list:0!string=string", "double!double=1.22", "true_bool!bool=-1", -// "false_bool!bool=0", "false_bool2!bool=false", "true_bool2!bool=true", "--list:1=string1", -// "list__2!string=string2", "--object__inner:object=string", "--int_list:0___int=33", "--int_list:1___int=22", -// "int_list__2!int=11", -// }; -// auto provider = sb::cf::CommandLineConfigurationSource::create(args)->build(mock); -// -// provider->load(); -// -// sb::cf::JsonObject expected = {{"string", "test"}, -// {"double", 1.22}, -// {"false_bool", false}, -// {"false_bool2", false}, -// {"true_bool", true}, -// {"true_bool2", true}, -// {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, -// {"int_list", sb::cf::JsonArray{33, 22, 11}}, -// {"object", {{"inner", {{"object", "string"}}}}}}; -// -// EXPECT_EQ(provider->getConfiguration(), expected); -// } -// -// TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) -// { -// const char *argv[] = { -// "exec/path", -// }; -// int size = sizeof(argv) / sizeof(char *); -// auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv)->build(mock); -// -// provider->load(); -// -// EXPECT_TRUE(provider->getConfiguration().empty()); -// } diff --git a/Tests/Unit/CommandLineParserBuilderTest.cpp b/Tests/Unit/CommandLineParserBuilderTest.cpp new file mode 100644 index 0000000..aa4668c --- /dev/null +++ b/Tests/Unit/CommandLineParserBuilderTest.cpp @@ -0,0 +1,133 @@ +#include +#include + +#include "Mocks/DeserializerMock.hpp" +#include "Mocks/SettingSplitterMock.hpp" +#include "Mocks/ValueDeserializersMapMock.hpp" +#include "SevenBit/Conf/CommandLineParserBuilder.hpp" +#include "SevenBit/Conf/Details/CommandLineParser.hpp" +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" + +class CommandLineParserBuilderTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + CommandLineParserBuilderTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(CommandLineParserBuilderTest, ShouldBuildDefault) +{ + auto parser = sb::cf::CommandLineParserBuilder{}.build(); + + auto &casted = dynamic_cast(*parser); + + auto &splitter = dynamic_cast(casted.getOptionsSplitter()); + auto &deserializers = + dynamic_cast(casted.getValueDeserializersMap()); + + EXPECT_EQ(casted.getOptionPrefixes(), (std::vector{"--", "/"})); + EXPECT_TRUE(casted.getConsiderSeparated()); + EXPECT_EQ(splitter.getKeySplitters(), (std::vector{":"})); + EXPECT_EQ(splitter.getSettingSplitters(), (std::vector{"="})); + EXPECT_EQ(splitter.getTypeMarkers(), (std::vector{"!"})); + EXPECT_FALSE(splitter.getAllowEmptyKeys()); + + std::vector types; + for (auto &[type, _] : deserializers.getDeserializersMap()) + { + types.push_back(type); + } + std::sort(types.begin(), types.end()); + EXPECT_EQ(types, (std::vector{"bool", "double", "int", "json", "null", "string", "uint"})); + EXPECT_EQ(deserializers.getDefaultType(), "string"); + EXPECT_TRUE(deserializers.getThrowOnUnknownType()); +} + +TEST_F(CommandLineParserBuilderTest, ShouldUseCustomConfig) +{ + sb::cf::CommandLineParserBuilder builder; + + sb::cf::CommandLineParserConfig config; + config.defaultType = "int"; + config.throwOnUnknownType = false; + config.allowEmptyKeys = true; + config.keySplitters = {"/", "++"}; + config.optionSplitters = {"--", "%"}; + config.optionPrefixes = {"@@", "**"}; + config.typeMarkers = {"$", "##"}; + auto parser = builder.useConfig(config).build(); + + auto &casted = dynamic_cast(*parser); + + auto &splitter = dynamic_cast(casted.getOptionsSplitter()); + auto &deserializers = + dynamic_cast(casted.getValueDeserializersMap()); + + EXPECT_EQ(casted.getOptionPrefixes(), (std::vector{"@@", "**"})); + EXPECT_FALSE(casted.getConsiderSeparated()); + EXPECT_EQ(splitter.getKeySplitters(), (std::vector{"/", "++"})); + EXPECT_EQ(splitter.getSettingSplitters(), (std::vector{"--", "%"})); + EXPECT_EQ(splitter.getTypeMarkers(), (std::vector{"$", "##"})); + EXPECT_TRUE(splitter.getAllowEmptyKeys()); + + std::vector types; + for (auto &[type, _] : deserializers.getDeserializersMap()) + { + types.push_back(type); + } + std::sort(types.begin(), types.end()); + EXPECT_EQ(types, (std::vector{"bool", "double", "int", "json", "null", "string", "uint"})); + EXPECT_EQ(deserializers.getDefaultType(), "int"); + EXPECT_FALSE(deserializers.getThrowOnUnknownType()); +} + +TEST_F(CommandLineParserBuilderTest, ShouldUseValueDeserializer) +{ + sb::cf::CommandLineParserBuilder builder; + + auto parser = builder.useDefaultValueDeserializers() + .useValueDeserializer("newType", std::make_unique()) + .build(); + + auto &casted = dynamic_cast(*parser); + + auto &deserializers = casted.getValueDeserializersMap(); + auto get = [&](std::string_view type) { auto &_ = deserializers.getDeserializerFor(type); }; + EXPECT_TRUE(dynamic_cast(&casted.getOptionsSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_NO_THROW(get("newType")); + EXPECT_NO_THROW(get("int")); + EXPECT_THROW(get("unknown"), sb::cf::ConfigException); +} + +TEST_F(CommandLineParserBuilderTest, ShouldUseCustomValueDeserializerMap) +{ + sb::cf::CommandLineParserBuilder builder; + + auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getOptionsSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +} + +TEST_F(CommandLineParserBuilderTest, ShouldUseCustomSplitter) +{ + sb::cf::CommandLineParserBuilder builder; + + auto parser = builder.useSplitter(std::make_unique()).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getOptionsSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +} diff --git a/Tests/Unit/CommandLineParserTest.cpp b/Tests/Unit/CommandLineParserTest.cpp deleted file mode 100644 index f175f46..0000000 --- a/Tests/Unit/CommandLineParserTest.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// #include -// #include -// #include -// -// #include "Mocks/DeserializerMock.hpp" -// #include "Mocks/SettingSplitterMock.hpp" -// #include "Mocks/ValueDeserializersMapMock.hpp" -// #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" -// #include "SevenBit/Conf/Exceptions.hpp" -// -// class EnvironmentVarsParserTest : public testing::Test -// { -// protected: -// static void TearUpTestSuite() {} -// -// EnvironmentVarsParserTest() {} -// -// void SetUp() override {} -// -// void TearDown() override {} -// -// static void TearDownTestSuite() {} -// }; -// -// TEST_F(EnvironmentVarsParserTest, ShouldParseSetting) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!int=123"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = 123; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, -// true}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, 123})); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_FALSE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullSplitter) -// { -// sb::cf::ISettingSplitter::Ptr splitter; -// auto deserializers = std::make_unique(); -// -// EXPECT_THROW( -// (sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers), "string", false, -// true}), sb::cf::ConfigException); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullDeserializers) -// { -// sb::cf::IValueDeserializersMap::Ptr deserializers; -// auto splitter = std::make_unique(); -// -// EXPECT_THROW( -// (sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers), "string", false, -// true}), sb::cf::ConfigException); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = "value"; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, -// true}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); -// EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--!string=value"; -// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = "value"; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// true}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueEmptyKey) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--!string=value"; -// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, -// true}; -// -// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_FALSE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultTypeForUnknownType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!unknown=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor) -// .WillOnce(testing::Return(nullptr)) -// .WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = "value"; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// false}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_FALSE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!unknown=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// true}; -// -// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownDefaultType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!unknown=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); -// sb::cf::JsonValue returnedValue = "value"; -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// false}; -// -// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_FALSE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } diff --git a/Tests/Unit/Details/CommandLineParserTest.cpp b/Tests/Unit/Details/CommandLineParserTest.cpp new file mode 100644 index 0000000..98204a5 --- /dev/null +++ b/Tests/Unit/Details/CommandLineParserTest.cpp @@ -0,0 +1,98 @@ +#include +#include +#include + +#include "Mocks/DeserializerMock.hpp" +#include "Mocks/SettingSplitterMock.hpp" +#include "Mocks/ValueDeserializersMapMock.hpp" +#include "SevenBit/Conf/Details/CommandLineParser.hpp" +#include "SevenBit/Conf/Exceptions.hpp" + +class CommandLineParserTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + CommandLineParserTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(CommandLineParserTest, ShouldParseSetting) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::vector settings = {"--option:deep:deep!string=123", "--option2=123"}; + sb::cf::ISettingSplitter::Result returned1 = {{"option", "deep", "deep"}, "string", "123"}; + sb::cf::ISettingSplitter::Result returned2 = {{"option2"}, std::nullopt, "123"}; + EXPECT_CALL(*splitter, split(std::string_view{"option:deep:deep!string=123"})).WillOnce(testing::Return(returned1)); + EXPECT_CALL(*splitter, split(std::string_view{"option2=123"})).WillOnce(testing::Return(returned2)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::ReturnRef(deserializer)); + sb::cf::JsonValue returnedValue1 = "123"; + EXPECT_CALL(deserializer, deserialize).WillRepeatedly(testing::Return(returnedValue1)); + + sb::cf::details::CommandLineParser parser{std::move(splitter), std::move(deserializers), {"--"}, false}; + + EXPECT_EQ(parser.parse(settings), + (sb::cf::JsonObject{{"option2", "123"}, {"option", {{"deep", {{"deep", "123"}}}}}})); +} + +TEST_F(CommandLineParserTest, ShouldParseSeparatedSetting) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::vector settings = {"--option:deep:deep!int", "123"}; + sb::cf::ISettingSplitter::Result returned1 = {{"option", "deep", "deep"}, "int", std::nullopt}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned1)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::ReturnRef(deserializer)); + sb::cf::JsonValue returnedValue1 = 123; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue1)); + + sb::cf::details::CommandLineParser parser{std::move(splitter), std::move(deserializers), {"--"}, true}; + + EXPECT_EQ(parser.parse(settings), (sb::cf::JsonObject{{"option", {{"deep", {{"deep", 123}}}}}})); +} + +TEST_F(CommandLineParserTest, ShouldParseSeparatedEndSetting) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::vector settings = {"--option:deep:deep!int"}; + sb::cf::ISettingSplitter::Result returned1 = {{"option", "deep", "deep"}, "int", std::nullopt}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned1)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::ReturnRef(deserializer)); + sb::cf::JsonValue returnedValue1 = 0; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue1)); + + sb::cf::details::CommandLineParser parser{std::move(splitter), std::move(deserializers), {"--"}, true}; + + EXPECT_EQ(parser.parse(settings), (sb::cf::JsonObject{{"option", {{"deep", {{"deep", 0}}}}}})); +} + +TEST_F(CommandLineParserTest, ShouldFailCreateSettingParserDueNullSplitter) +{ + sb::cf::ISettingSplitter::Ptr splitter; + auto deserializers = std::make_unique(); + + EXPECT_THROW((sb::cf::details::CommandLineParser{std::move(splitter), std::move(deserializers), {"--"}}), + sb::cf::ConfigException); +} + +TEST_F(CommandLineParserTest, ShouldFailCreateSettingParserDueNullDeserializers) +{ + sb::cf::IValueDeserializersMap::Ptr deserializers; + auto splitter = std::make_unique(); + + EXPECT_THROW((sb::cf::details::CommandLineParser{std::move(splitter), std::move(deserializers), {"--"}}), + sb::cf::ConfigException); +} diff --git a/Tests/Unit/DeserializersTest.cpp b/Tests/Unit/Details/DeserializersTest.cpp similarity index 97% rename from Tests/Unit/DeserializersTest.cpp rename to Tests/Unit/Details/DeserializersTest.cpp index a39e759..a093b63 100644 --- a/Tests/Unit/DeserializersTest.cpp +++ b/Tests/Unit/Details/DeserializersTest.cpp @@ -5,9 +5,9 @@ #include #include -#include "SevenBit/Conf/Details/Deserializers.hpp" -#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -#include "Utilities/ParamsTest.hpp" +#include "../../../Include/SevenBit/Conf/Details/Deserializers.hpp" +#include "../../../Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp" +#include "../../Helpers/Utilities/ParamsTest.hpp" class DeserializersTest : public testing::Test { diff --git a/Tests/Unit/Details/EnvironmentVarsParserTest.cpp b/Tests/Unit/Details/EnvironmentVarsParserTest.cpp new file mode 100644 index 0000000..785093a --- /dev/null +++ b/Tests/Unit/Details/EnvironmentVarsParserTest.cpp @@ -0,0 +1,59 @@ +#include +#include +#include + +#include "Mocks/DeserializerMock.hpp" +#include "Mocks/SettingSplitterMock.hpp" +#include "Mocks/ValueDeserializersMapMock.hpp" +#include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" +#include "SevenBit/Conf/Exceptions.hpp" + +class EnvironmentVarsParserTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + EnvironmentVarsParserTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(EnvironmentVarsParserTest, ShouldParseSetting) +{ + DeserializerMock deserializer; + auto deserializers = std::make_unique(); + auto splitter = std::make_unique(); + + std::vector settings = {"--option:deep:deep!int=123"}; + sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; + EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); + EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::ReturnRef(deserializer)); + sb::cf::JsonValue returnedValue = 123; + EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); + + sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers)}; + + EXPECT_EQ(parser.parse(settings), (sb::cf::JsonObject{{"option", {{"deep", {{"deep", 123}}}}}})); +} + +TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullSplitter) +{ + sb::cf::ISettingSplitter::Ptr splitter; + auto deserializers = std::make_unique(); + + EXPECT_THROW((sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers)}), + sb::cf::ConfigException); +} + +TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullDeserializers) +{ + sb::cf::IValueDeserializersMap::Ptr deserializers; + auto splitter = std::make_unique(); + + EXPECT_THROW((sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers)}), + sb::cf::ConfigException); +} diff --git a/Tests/Unit/JsonObjectExtTest.cpp b/Tests/Unit/Details/JsonObjectExtTest.cpp similarity index 98% rename from Tests/Unit/JsonObjectExtTest.cpp rename to Tests/Unit/Details/JsonObjectExtTest.cpp index e65eae1..fef549e 100644 --- a/Tests/Unit/JsonObjectExtTest.cpp +++ b/Tests/Unit/Details/JsonObjectExtTest.cpp @@ -1,9 +1,9 @@ #include #include -#include "SevenBit/Conf/Details/JsonExt.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "Utilities/ParamsTest.hpp" +#include "../../../Include/SevenBit/Conf/Details/JsonExt.hpp" +#include "../../../Include/SevenBit/Conf/Exceptions.hpp" +#include "../../Helpers/Utilities/ParamsTest.hpp" using namespace sb::cf::json; diff --git a/Tests/Unit/SettingSplitterTest.cpp b/Tests/Unit/Details/SettingSplitterTest.cpp similarity index 96% rename from Tests/Unit/SettingSplitterTest.cpp rename to Tests/Unit/Details/SettingSplitterTest.cpp index 83f3c90..93f59e1 100644 --- a/Tests/Unit/SettingSplitterTest.cpp +++ b/Tests/Unit/Details/SettingSplitterTest.cpp @@ -1,9 +1,9 @@ #include #include -#include "SevenBit/Conf/Details/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/StringUtils.hpp" -#include "Utilities/ParamsTest.hpp" +#include "../../../Include/SevenBit/Conf/Details/SettingSplitter.hpp" +#include "../../../Include/SevenBit/Conf/Details/StringUtils.hpp" +#include "../../Helpers/Utilities/ParamsTest.hpp" class SettingSplitterTest : public testing::Test { diff --git a/Tests/Unit/UtilsTest.cpp b/Tests/Unit/Details/UtilsTest.cpp similarity index 99% rename from Tests/Unit/UtilsTest.cpp rename to Tests/Unit/Details/UtilsTest.cpp index 1122f78..a7e17de 100644 --- a/Tests/Unit/UtilsTest.cpp +++ b/Tests/Unit/Details/UtilsTest.cpp @@ -1,8 +1,8 @@ #include #include -#include "SevenBit/Conf/Details/StringUtils.hpp" -#include "Utilities/ParamsTest.hpp" +#include "../../../Include/SevenBit/Conf/Details/StringUtils.hpp" +#include "../../Helpers/Utilities/ParamsTest.hpp" class UtilsTest : public testing::Test { diff --git a/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp b/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp new file mode 100644 index 0000000..8d7d585 --- /dev/null +++ b/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp @@ -0,0 +1,128 @@ +#include +#include + +#include "Mocks/DeserializerMock.hpp" +#include "Mocks/SettingSplitterMock.hpp" +#include "Mocks/ValueDeserializersMapMock.hpp" +#include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" +#include "SevenBit/Conf/Details/SettingSplitter.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" +#include "SevenBit/Conf/EnvironmentVarsParserBuilder.hpp" + +class EnvironmentVarsParserBuilderTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + EnvironmentVarsParserBuilderTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(EnvironmentVarsParserBuilderTest, ShouldBuildDefault) +{ + auto parser = sb::cf::EnvironmentVarsParserBuilder{}.build(); + + auto &casted = dynamic_cast(*parser); + + auto &splitter = dynamic_cast(casted.getSettingSplitter()); + auto &deserializers = + dynamic_cast(casted.getValueDeserializersMap()); + + EXPECT_EQ(splitter.getKeySplitters(), (std::vector{":", "__"})); + EXPECT_EQ(splitter.getSettingSplitters(), (std::vector{"="})); + EXPECT_EQ(splitter.getTypeMarkers(), (std::vector{"!", "___"})); + EXPECT_FALSE(splitter.getAllowEmptyKeys()); + + std::vector types; + for (auto &[type, _] : deserializers.getDeserializersMap()) + { + types.push_back(type); + } + std::sort(types.begin(), types.end()); + EXPECT_EQ(types, (std::vector{"bool", "double", "int", "json", "null", "string", "uint"})); + EXPECT_EQ(deserializers.getDefaultType(), "string"); + EXPECT_TRUE(deserializers.getThrowOnUnknownType()); +} + +TEST_F(EnvironmentVarsParserBuilderTest, ShouldUseCustomConfig) +{ + sb::cf::EnvironmentVarsParserBuilder builder; + + sb::cf::EnvironmentVarsParserConfig config; + config.defaultType = "int"; + config.throwOnUnknownType = false; + config.allowEmptyKeys = true; + config.keySplitters = {"/", "++"}; + config.variableSplitters = {"--", "%"}; + config.typeMarkers = {"$", "##"}; + auto parser = builder.useConfig(config).build(); + + auto &casted = dynamic_cast(*parser); + + auto &splitter = dynamic_cast(casted.getSettingSplitter()); + auto &deserializers = + dynamic_cast(casted.getValueDeserializersMap()); + + EXPECT_EQ(splitter.getKeySplitters(), (std::vector{"/", "++"})); + EXPECT_EQ(splitter.getSettingSplitters(), (std::vector{"--", "%"})); + EXPECT_EQ(splitter.getTypeMarkers(), (std::vector{"$", "##"})); + EXPECT_TRUE(splitter.getAllowEmptyKeys()); + + std::vector types; + for (auto &[type, _] : deserializers.getDeserializersMap()) + { + types.push_back(type); + } + std::sort(types.begin(), types.end()); + EXPECT_EQ(types, (std::vector{"bool", "double", "int", "json", "null", "string", "uint"})); + EXPECT_EQ(deserializers.getDefaultType(), "int"); + EXPECT_FALSE(deserializers.getThrowOnUnknownType()); +} + +TEST_F(EnvironmentVarsParserBuilderTest, ShouldUseValueDeserializer) +{ + sb::cf::EnvironmentVarsParserBuilder builder; + + auto parser = builder.useDefaultValueDeserializers() + .useValueDeserializer("newType", std::make_unique()) + .build(); + + auto &casted = dynamic_cast(*parser); + + auto &deserializers = casted.getValueDeserializersMap(); + auto get = [&](std::string_view type) { auto &_ = deserializers.getDeserializerFor(type); }; + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); + EXPECT_NO_THROW(get("newType")); + EXPECT_NO_THROW(get("int")); + EXPECT_THROW(get("unknown"), sb::cf::ConfigException); +} + +TEST_F(EnvironmentVarsParserBuilderTest, ShouldUseCustomValueDeserializerMap) +{ + sb::cf::EnvironmentVarsParserBuilder builder; + + auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +} + +TEST_F(EnvironmentVarsParserBuilderTest, ShouldUseCustomSplitter) +{ + sb::cf::EnvironmentVarsParserBuilder builder; + + auto parser = builder.useSplitter(std::make_unique()).build(); + + auto &casted = dynamic_cast(*parser); + + EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); + EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); +} diff --git a/Tests/Unit/ObjectHolderTest.cpp b/Tests/Unit/ObjectHolderTest.cpp new file mode 100644 index 0000000..b81c6fc --- /dev/null +++ b/Tests/Unit/ObjectHolderTest.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include "SevenBit/Conf/ObjectHolder.hpp" + +class ObjectHolderTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + ObjectHolderTest() {} + + void SetUp() override {} + + void TearDown() override {} + + ~ObjectHolderTest() override = default; + + static void TearDownTestSuite() {} +}; + +TEST_F(ObjectHolderTest, ShouldCastHolder) +{ + const sb::cf::IObject::Ptr holder = sb::cf::ObjectHolder::from(1); + + auto &casted = sb::cf::ObjectHolder::castFrom(*holder); + auto &casted2 = sb::cf::ObjectHolder::safeCastFrom(*holder); + + EXPECT_EQ(casted.get(), 1); + + auto act = [&] { auto &_ = sb::cf::ObjectHolder::safeCastFrom(*holder); }; + EXPECT_THROW(act(), std::bad_cast); +} diff --git a/Tests/Unit/SettingParserBuilderTest.cpp b/Tests/Unit/SettingParserBuilderTest.cpp deleted file mode 100644 index 0ea32aa..0000000 --- a/Tests/Unit/SettingParserBuilderTest.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// #include -// #include -// -// #include "Mocks/DeserializerMock.hpp" -// #include "Mocks/SettingSplitterMock.hpp" -// #include "Mocks/ValueDeserializersMapMock.hpp" -// #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" -// #include "SevenBit/Conf/Details/SettingSplitter.hpp" -// #include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" -// #include "SevenBit/Conf/EnvironmentVarsParserBuilder.hpp" -// -// class SettingParserBuilderTest : public testing::Test -// { -// protected: -// static void TearUpTestSuite() {} -// -// SettingParserBuilderTest() {} -// -// void SetUp() override {} -// -// void TearDown() override {} -// -// static void TearDownTestSuite() {} -// }; -// -// TEST_F(SettingParserBuilderTest, ShouldBuildDefault) -// { -// sb::cf::SettingParserBuilder builder; -// -// auto parser = builder.build(); -// -// auto &casted = dynamic_cast(*parser); -// -// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); -// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); -// EXPECT_EQ(casted.getDefaultType(), "string"); -// EXPECT_FALSE(casted.getAllowEmptyKeys()); -// EXPECT_TRUE(casted.getThrowOnUnknownType()); -// } -// -// TEST_F(SettingParserBuilderTest, ShouldUseCustomConfig) -// { -// sb::cf::SettingParserBuilder builder; -// -// sb::cf::EnvironmentVarsParserConfig config; -// config.defaultType = "int"; -// config.throwOnUnknownType = false; -// config.allowEmptyKeys = true; -// auto parser = builder.useConfig(config).build(); -// -// auto &casted = dynamic_cast(*parser); -// -// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); -// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); -// EXPECT_EQ(casted.getDefaultType(), "int"); -// EXPECT_TRUE(casted.getAllowEmptyKeys()); -// EXPECT_FALSE(casted.getThrowOnUnknownType()); -// } -// -// TEST_F(SettingParserBuilderTest, ShouldUseValueDeserializer) -// { -// sb::cf::SettingParserBuilder builder; -// -// auto parser = builder.useDefaultValueDeserializers() -// .useValueDeserializer("newType", std::make_unique()) -// .build(); -// -// auto &casted = dynamic_cast(*parser); -// -// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); -// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); -// EXPECT_EQ(casted.getDefaultType(), "string"); -// EXPECT_FALSE(casted.getAllowEmptyKeys()); -// EXPECT_TRUE(casted.getThrowOnUnknownType()); -// EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("newType")); -// EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("int")); -// EXPECT_TRUE(casted.getValueDeserializersMap().getDeserializerFor("bool")); -// } -// -// TEST_F(SettingParserBuilderTest, ShouldUseCustomValueDeserializerMap) -// { -// sb::cf::SettingParserBuilder builder; -// -// auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); -// -// auto &casted = dynamic_cast(*parser); -// -// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); -// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); -// EXPECT_EQ(casted.getDefaultType(), "string"); -// EXPECT_FALSE(casted.getAllowEmptyKeys()); -// EXPECT_TRUE(casted.getThrowOnUnknownType()); -// } -// -// TEST_F(SettingParserBuilderTest, ShouldUseCustomSplitter) -// { -// sb::cf::SettingParserBuilder builder; -// -// auto parser = builder.useSplitter(std::make_unique()).build(); -// -// auto &casted = dynamic_cast(*parser); -// -// EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); -// EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); -// EXPECT_EQ(casted.getDefaultType(), "string"); -// EXPECT_FALSE(casted.getAllowEmptyKeys()); -// EXPECT_TRUE(casted.getThrowOnUnknownType()); -// } diff --git a/Tests/Unit/SettingParserTest.cpp b/Tests/Unit/SettingParserTest.cpp deleted file mode 100644 index 390dc96..0000000 --- a/Tests/Unit/SettingParserTest.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// #include -// #include -// #include -// -// #include "Mocks/DeserializerMock.hpp" -// #include "Mocks/SettingSplitterMock.hpp" -// #include "Mocks/ValueDeserializersMapMock.hpp" -// #include "SevenBit/Conf/Details/EnvironmentVarsParser.hpp" -// #include "SevenBit/Conf/Exceptions.hpp" -// -// class EnvironmentVarsParserTest : public testing::Test -// { -// protected: -// static void TearUpTestSuite() {} -// -// EnvironmentVarsParserTest() {} -// -// void SetUp() override {} -// -// void TearDown() override {} -// -// static void TearDownTestSuite() {} -// }; -// -// TEST_F(EnvironmentVarsParserTest, ShouldParseSetting) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::vector settings = {"--option:deep:deep!int=123"}; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "int", "123"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = 123; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers)}; -// -// EXPECT_EQ(parser.parse(settings), (sb::cf::JsonObject{{"option", {{"deep", {{"deep", 123}}}}}})); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullSplitter) -// { -// sb::cf::ISettingSplitter::Ptr splitter; -// auto deserializers = std::make_unique(); -// -// EXPECT_THROW((sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers)}), -// sb::cf::ConfigException); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueNullDeserializers) -// { -// sb::cf::IValueDeserializersMap::Ptr deserializers; -// auto splitter = std::make_unique(); -// -// EXPECT_THROW((sb::cf::details::EnvironmentVarsParser{std::move(splitter), std::move(deserializers)}), -// sb::cf::ConfigException); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, std::nullopt, "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = "value"; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, -// true}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); -// EXPECT_EQ(parser.getDefaultType(), std::string_view{"string"}); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldNotFailCreateSettingParserDueEmptyKey) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--!string=value"; -// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = "value"; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// true}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{""}, "value"})); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailCreateSettingParserDueEmptyKey) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--!string=value"; -// sb::cf::ISettingSplitter::Result returned = {{""}, "string", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", false, -// true}; -// -// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_FALSE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldUseDefaultTypeForUnknownType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!unknown=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor) -// .WillOnce(testing::Return(nullptr)) -// .WillOnce(testing::Return(&deserializer)); -// sb::cf::JsonValue returnedValue = "value"; -// EXPECT_CALL(deserializer, deserialize).WillOnce(testing::Return(returnedValue)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// false}; -// -// EXPECT_EQ(parser.parse(setting), (sb::cf::ISettingParser::Result{{"option", "deep", "deep"}, "value"})); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_FALSE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!unknown=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillOnce(testing::Return(nullptr)); -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// true}; -// -// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_TRUE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } -// -// TEST_F(EnvironmentVarsParserTest, ShouldFailDueToForUnknownDefaultType) -// { -// DeserializerMock deserializer; -// auto deserializers = std::make_unique(); -// auto splitter = std::make_unique(); -// -// std::string_view setting = "--option:deep:deep!unknown=value"; -// sb::cf::ISettingSplitter::Result returned = {{"option", "deep", "deep"}, "unknown", "value"}; -// EXPECT_CALL(*splitter, split).WillOnce(testing::Return(returned)); -// EXPECT_CALL(*deserializers, getDeserializerFor).WillRepeatedly(testing::Return(nullptr)); -// sb::cf::JsonValue returnedValue = "value"; -// -// sb::cf::details::EnvironmentVarsParser parser{std::move(splitter), std::move(deserializers), "string", true, -// false}; -// -// EXPECT_THROW(auto result = parser.parse(setting), sb::cf::ConfigException); -// EXPECT_EQ(parser.getDefaultType(), "string"); -// EXPECT_FALSE(parser.getThrowOnUnknownType()); -// EXPECT_TRUE(parser.getAllowEmptyKeys()); -// } diff --git a/Tests/Unit/AppSettingsConfigurationTest.cpp b/Tests/Unit/Sources/AppSettingsConfigurationTest.cpp similarity index 100% rename from Tests/Unit/AppSettingsConfigurationTest.cpp rename to Tests/Unit/Sources/AppSettingsConfigurationTest.cpp diff --git a/Tests/Unit/ChainedConfigurationTest.cpp b/Tests/Unit/Sources/ChainedConfigurationTest.cpp similarity index 100% rename from Tests/Unit/ChainedConfigurationTest.cpp rename to Tests/Unit/Sources/ChainedConfigurationTest.cpp diff --git a/Tests/Unit/Sources/CommandLineConfigurationTest.cpp b/Tests/Unit/Sources/CommandLineConfigurationTest.cpp new file mode 100644 index 0000000..4bba1b4 --- /dev/null +++ b/Tests/Unit/Sources/CommandLineConfigurationTest.cpp @@ -0,0 +1,123 @@ +#include +#include + +#include "Mocks/ConfigurationBuilderMock.hpp" +#include "SevenBit/Conf/Exceptions.hpp" +#include "SevenBit/Conf/Sources/CommandLineConfiguration.hpp" + +class CommandLineConfigurationTest : public testing::Test +{ + protected: + ConfigurationBuilderMock mock; + + static void TearUpTestSuite() {} + + CommandLineConfigurationTest() {} + + void SetUp() override {} + + void TearDown() override {} + + static void TearDownTestSuite() {} +}; + +TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) +{ + std::vector args; + EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), + sb::cf::NullPointerException); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) +{ + const char *const argv[] = {"program/path", + "--string!string=test", + "--string_sep!string", + "sep_value", + "--list:0!string=string", + "double!double=1.22", + "true_bool!bool=-1", + "false_bool!bool=0", + "false_bool2!bool=false", + "true_bool2!bool=true", + "--list:1=string1", + "list:2!string=string2", + "--object:inner:object=string", + "--int_list:0!int=33", + "--int_list:1!int=22", + "int_list:2!int=11", + "--last!int"}; + int size = sizeof(argv) / sizeof(char *); + auto provider = + sb::cf::CommandLineConfigurationSource::create(size, argv, sb::cf::CommandLineParserBuilder{}.build()) + ->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"string", "test"}, + {"string_sep", "sep_value"}, + {"double", 1.22}, + {"false_bool", false}, + {"false_bool2", false}, + {"true_bool", true}, + {"true_bool2", true}, + {"last", 0}, + {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"int_list", sb::cf::JsonArray{33, 22, 11}}, + {"object", {{"inner", {{"object", "string"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) +{ + std::vector args = {"--string!string=test", + "--string_sep!string", + "sep_value", + "--list:0!string=string", + "double!double=1.22", + "true_bool!bool=-1", + "false_bool!bool=0", + "false_bool2!bool=false", + "true_bool2!bool=true", + "--list:1=string1", + "list:2!string=string2", + "--object:inner:object=string", + "--int_list:0!int=33", + "--int_list:1!int=22", + "int_list:2!int=11", + "--last!int"}; + auto provider = + sb::cf::CommandLineConfigurationSource::create(args, sb::cf::CommandLineParserBuilder{}.build())->build(mock); + + provider->load(); + + sb::cf::JsonObject expected = {{"string", "test"}, + {"double", 1.22}, + {"string_sep", "sep_value"}, + {"false_bool", false}, + {"false_bool2", false}, + {"true_bool", true}, + {"true_bool2", true}, + {"last", 0}, + {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"int_list", sb::cf::JsonArray{33, 22, 11}}, + {"object", {{"inner", {{"object", "string"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) +{ + const char *argv[] = { + "exec/path", + }; + int size = sizeof(argv) / sizeof(char *); + auto provider = + sb::cf::CommandLineConfigurationSource::create(size, argv, sb::cf::CommandLineParserBuilder{}.build()) + ->build(mock); + + provider->load(); + + EXPECT_TRUE(provider->getConfiguration().empty()); +} diff --git a/Tests/Unit/EnvironmentVarsConfigurationTest.cpp b/Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp similarity index 83% rename from Tests/Unit/EnvironmentVarsConfigurationTest.cpp rename to Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp index df86075..106a192 100644 --- a/Tests/Unit/EnvironmentVarsConfigurationTest.cpp +++ b/Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp @@ -1,10 +1,9 @@ #include #include -#include "Mocks/ConfigurationBuilderMock.hpp" -#include "SevenBit/Conf/Exceptions.hpp" -#include "SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" - +#include "../../../Include/SevenBit/Conf/Exceptions.hpp" +#include "../../../Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp" +#include "../../Helpers/Mocks/ConfigurationBuilderMock.hpp" #ifdef _WIN32 #define _7BIT_CONF_PUT_ENV _putenv @@ -55,7 +54,9 @@ TEST_F(EnvironmentVarsConfigurationTest, ShouldFailProviderCreationDueToNullSour TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVars) { - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_")->build(mock); + auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_", + sb::cf::EnvironmentVarsParserBuilder{}.build()) + ->build(mock); provider->load(); @@ -72,7 +73,9 @@ TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVars) TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVarsWithPrefix) { - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_")->build(mock); + auto provider = + sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_", sb::cf::EnvironmentVarsParserBuilder{}.build()) + ->build(mock); provider->load(); @@ -90,7 +93,9 @@ TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVarsWithPrefix) TEST_F(EnvironmentVarsConfigurationTest, ShouldNotLoadConfFromEnvVars) { - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BITCONFIGURATION_")->build(mock); + auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BITCONFIGURATION_", + sb::cf::EnvironmentVarsParserBuilder{}.build()) + ->build(mock); provider->load(); diff --git a/Tests/Unit/InMemoryConfigurationTest.cpp b/Tests/Unit/Sources/InMemoryConfigurationTest.cpp similarity index 100% rename from Tests/Unit/InMemoryConfigurationTest.cpp rename to Tests/Unit/Sources/InMemoryConfigurationTest.cpp diff --git a/Tests/Unit/JsonConfigurationTest.cpp b/Tests/Unit/Sources/JsonConfigurationTest.cpp similarity index 100% rename from Tests/Unit/JsonConfigurationTest.cpp rename to Tests/Unit/Sources/JsonConfigurationTest.cpp diff --git a/Tests/Unit/JsonFileConfigurationTest.cpp b/Tests/Unit/Sources/JsonFileConfigurationTest.cpp similarity index 100% rename from Tests/Unit/JsonFileConfigurationTest.cpp rename to Tests/Unit/Sources/JsonFileConfigurationTest.cpp diff --git a/Tests/Unit/JsonStreamConfigurationTest.cpp b/Tests/Unit/Sources/JsonStreamConfigurationTest.cpp similarity index 100% rename from Tests/Unit/JsonStreamConfigurationTest.cpp rename to Tests/Unit/Sources/JsonStreamConfigurationTest.cpp diff --git a/Tests/Unit/KeyPerFileConfigurationTest.cpp b/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp similarity index 100% rename from Tests/Unit/KeyPerFileConfigurationTest.cpp rename to Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp diff --git a/Tests/Unit/MapConfigutationTest.cpp b/Tests/Unit/Sources/MapConfigutationTest.cpp similarity index 100% rename from Tests/Unit/MapConfigutationTest.cpp rename to Tests/Unit/Sources/MapConfigutationTest.cpp From fa2020cfa874a78f71c98d9ec2a35149cfdfed52 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 18:42:56 +0100 Subject: [PATCH 09/31] update CI --- .github/actions/cmake-build/action.yml | 37 ----------- .github/actions/conan-install/action.yml | 36 ---------- .github/workflows/CI.yml | 39 ----------- .github/workflows/DevCI.yml | 38 +++++++++++ .github/workflows/Linux.yml | 84 ++++++++++++++++++++++++ .github/workflows/MacOs.yml | 61 +++++++++++++++++ .github/workflows/Windows.yml | 83 +++++++++++++++++++++++ Cmake/Setup.cmake | 66 +++++++++---------- 8 files changed, 299 insertions(+), 145 deletions(-) delete mode 100644 .github/actions/cmake-build/action.yml delete mode 100644 .github/actions/conan-install/action.yml delete mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/DevCI.yml create mode 100644 .github/workflows/Linux.yml create mode 100644 .github/workflows/MacOs.yml create mode 100644 .github/workflows/Windows.yml diff --git a/.github/actions/cmake-build/action.yml b/.github/actions/cmake-build/action.yml deleted file mode 100644 index 4b54027..0000000 --- a/.github/actions/cmake-build/action.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Run CMake -description: "Runs CMake" - -inputs: - args: - description: "Other arguments" - required: false - default: -D_7BIT_CONF_BUILD_TESTS:BOOL=true -D_7BIT_CONF_BUILD_EXAMPLES:BOOL=true - library-type: - description: "Library type" - required: false - default: Static - -runs: - using: composite - steps: - - name: Set Proper Conan Profile - uses: kanga333/variable-mapper@v0.3.0 - with: - key: "${{ runner.os }}" - map: | - { - "Windows":{ - "initPreset": "conan-default" - }, - ".*":{ - "initPreset": "conan-release" - } - } - - - name: Configure CMake - run: cmake --preset ${{ env.initPreset }} -D_7BIT_CONF_LIBRARY_TYPE=${{ inputs.library-type }} ${{ inputs.args }} - shell: pwsh - - - name: Build - run: cmake --build --preset conan-release - shell: pwsh diff --git a/.github/actions/conan-install/action.yml b/.github/actions/conan-install/action.yml deleted file mode 100644 index 073c398..0000000 --- a/.github/actions/conan-install/action.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Run CMake -description: "Runs CMake" -inputs: - install-dir: - description: "Installation directory" - required: true - -runs: - using: composite - steps: - - name: Install Conan - uses: turtlebrowser/get-conan@v1.2 - - - name: Set Proper Conan Profile - uses: kanga333/variable-mapper@v0.3.0 - with: - key: "${{ runner.os }}" - map: | - { - "Linux":{ - "use_conan_profile":"linux-gcc-x86_64" - }, - "macOS":{ - "use_conan_profile":"mac-appple-clang-x86_64" - }, - "Windows":{ - "use_conan_profile":"windows-msvc-x86_64" - } - } - - name: Configure Conan - run: conan config install https://github.com/7bitcoder/conan-config.git - shell: pwsh - - - name: Install Conan Packages in ${{ inputs.install-dir }} - run: conan install . --output-folder=${{ inputs.install-dir }} --build=missing -pr=${{ env.use_conan_profile }} -pr:b=${{ env.use_conan_profile }} - shell: pwsh diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index a6bb334..0000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: CI - -on: - push: - branches: [ "dev", "main" ] - paths-ignore: - - ".readthedocs.yaml" - - "README.md" - -env: - BUILD_TYPE: Release - BUILD_DIR: ${{github.workspace}}/build - -jobs: - test: - strategy: - fail-fast: false - matrix: - libraryType: [ HeaderOnly, Static, Shared ] - os: [ ubuntu-22.04, macos-12, windows-2022 ] - - runs-on: ${{matrix.os}} - - steps: - - uses: actions/checkout@v4 - - - name: Install Conan Packages - id: conan - uses: ./.github/actions/conan-install - with: - install-dir: ${{ env.BUILD_DIR }} - - - name: CMake Build - uses: ./.github/actions/cmake-build - with: - library-type: ${{ matrix.libraryType }} - - - name: Test - run: ctest -j 10 --preset conan-release diff --git a/.github/workflows/DevCI.yml b/.github/workflows/DevCI.yml new file mode 100644 index 0000000..4e2b0e6 --- /dev/null +++ b/.github/workflows/DevCI.yml @@ -0,0 +1,38 @@ +name: DevCI + +on: + push: + branches: [ "dev" ] + paths-ignore: + - ".readthedocs.yaml" + - "README.md" + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ ubuntu-22.04, macos-12, windows-2022 ] + library_type: [ HeaderOnly, Static, Shared ] + std: [ 17, 20 ] + build_type: [ Release ] + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v4 + + - name: Create Build Environment + run: cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure + run: cmake -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} -D_7BIT_CONF_LIBRARY_TYPE=${{matrix.library_type}} -D_7BIT_CONF_BUILD_ALL_TESTS=ON -D_7BIT_CONF_BUILD_EXAMPLES=ON + + - name: Build + run: cmake --build ${{runner.workspace}}/build --config ${{matrix.build_type}} -j + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} + env: + CTEST_OUTPUT_ON_FAILURE: True diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml new file mode 100644 index 0000000..8aa1198 --- /dev/null +++ b/.github/workflows/Linux.yml @@ -0,0 +1,84 @@ +name: Linux + +on: + pull_request: + branches: [ "main" ] + paths-ignore: + - ".readthedocs.yaml" + - "README.md" + +jobs: + test: + strategy: + fail-fast: false + matrix: + compiler: + - { tool: gcc, ver: 7 } + - { tool: gcc, ver: 8 } + - { tool: gcc, ver: 9 } + - { tool: gcc, ver: 10 } + - { tool: gcc, ver: 11 } + - { tool: gcc, ver: 12 } + - { tool: gcc, ver: 13 } + - { tool: clang, ver: 7 } + - { tool: clang, ver: 8 } + - { tool: clang, ver: 9 } + - { tool: clang, ver: 10 } + - { tool: clang, ver: 11 } + - { tool: clang, ver: 12 } + - { tool: clang, ver: 13 } + - { tool: clang, ver: 14 } + - { tool: clang, ver: 15 } + build_type: [ Release ] + os: [ ubuntu-20.04, ubuntu-22.04 ] + std: [ 17 ] + library_type: [ Static ] + include: + - compiler: { tool: gcc } + cxx: g++ + cc: gcc + generator: Ninja + - compiler: { tool: clang } + cxx: clang++ + cc: clang + generator: Ninja + exclude: + - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 12 } } + - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 13 } } + - { os: ubuntu-20.04, compiler: { tool: clang, ver: 13 } } + - { os: ubuntu-20.04, compiler: { tool: clang, ver: 14 } } + - { os: ubuntu-20.04, compiler: { tool: clang, ver: 15 } } + - { os: ubuntu-22.04, compiler: { tool: gcc, ver: 7 } } + - { os: ubuntu-22.04, compiler: { tool: gcc, ver: 8 } } + - { os: ubuntu-22.04, compiler: { tool: clang, ver: 7 } } + - { os: ubuntu-22.04, compiler: { tool: clang, ver: 8 } } + - { os: ubuntu-22.04, compiler: { tool: clang, ver: 9 } } + - { os: ubuntu-22.04, compiler: { tool: clang, ver: 10 } } + + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + + - name: Create Build Environment + env: + PACKAGES: ${{ matrix.compiler.tool == 'gcc' && format('gcc-{0} g++-{0}', matrix.compiler.ver) || format('{0}-{1}', matrix.compiler.tool, matrix.compiler.ver) }} + run: | + sudo apt update + sudo apt install ${{env.PACKAGES}} ninja-build -y + sudo apt install locales-all + cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure + env: + CXX: ${{matrix.cxx}}-${{matrix.compiler.ver}} + CC: ${{matrix.cc}}-${{matrix.compiler.ver}} + run: cmake -B ${{runner.workspace}}/build -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} -D_7BIT_CONF_LIBRARY_TYPE=${{matrix.library_type}} -D_7BIT_CONF_BUILD_ALL_TESTS=ON + + - name: Build + run: cmake --build ${{runner.workspace}}/build --config ${{matrix.build_type}} -j + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} + env: + CTEST_OUTPUT_ON_FAILURE: True diff --git a/.github/workflows/MacOs.yml b/.github/workflows/MacOs.yml new file mode 100644 index 0000000..f4cbeac --- /dev/null +++ b/.github/workflows/MacOs.yml @@ -0,0 +1,61 @@ +name: MacOs + +on: + pull_request: + branches: [ "main" ] + paths-ignore: + - ".readthedocs.yaml" + - "README.md" + +jobs: + test: + strategy: + fail-fast: false + matrix: + compiler: + [ + { tool: apple-clang }, + { tool: gcc, ver: 9 }, + { tool: gcc, ver: 10 }, + { tool: gcc, ver: 11 }, + { tool: gcc, ver: 12 }, + { tool: gcc, ver: 13 }, + ] + build_type: [ Release ] + os: [ macos-12, macos-13 ] + std: [ 17 ] + library_type: [ Static ] + include: + - compiler: { tool: gcc } + cxx: g++ + cc: gcc + - compiler: { tool: apple-clang } + cxx: "" + cc: "" + exclude: + - { os: macos-12, compiler: { tool: gcc, ver: 9 } } + + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + + - name: Create Build Environment + if: matrix.compiler.tool != 'apple-clang' + run: | + brew update && + brew install ${{matrix.compiler.tool}}@${{matrix.compiler.ver}} ninja + cmake -E make_directory ${{runner.workspace}}/build + echo "CXX=${{matrix.cxx}}-${{matrix.compiler.ver}}" >> $GITHUB_ENV + echo "CC=${{matrix.cc}}-${{matrix.compiler.ver}}" >> $GITHUB_ENV + + - name: Configure + run: cmake -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} -D_7BIT_CONF_LIBRARY_TYPE=${{matrix.library_type}} -D_7BIT_CONF_BUILD_ALL_TESTS=ON + + - name: Build + run: cmake --build ${{runner.workspace}}/build --config ${{matrix.build_type}} -j + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} + env: + CTEST_OUTPUT_ON_FAILURE: True diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml new file mode 100644 index 0000000..e6d19f5 --- /dev/null +++ b/.github/workflows/Windows.yml @@ -0,0 +1,83 @@ +name: Windows + +on: + pull_request: + branches: [ "main" ] + paths-ignore: + - ".readthedocs.yaml" + - "README.md" + +env: + CHOCO_LIB_DIR: C:\\ProgramData\\chocolatey\\lib + +jobs: + test: + strategy: + fail-fast: false + matrix: + compiler: + [ + { tool: msvc, ver: 141 }, + { tool: msvc, ver: 142 }, + { tool: mingw, ver: 7.5.0 }, + { tool: mingw, ver: 8.5.0 }, + { tool: mingw, ver: 9.4.0 }, + { tool: mingw, ver: 10.3.0 }, + { tool: mingw, ver: 11.2.0 }, + { tool: mingw, ver: 12.2.0 }, + { tool: mingw, ver: 13.2.0 }, + { tool: LLVM, ver: 11.1.0 }, + { tool: LLVM, ver: 12.0.1 }, + { tool: LLVM, ver: 13.0.1 }, + { tool: LLVM, ver: 14.0.6 }, + { tool: LLVM, ver: 15.0.7 }, + { tool: LLVM, ver: 16.0.6 }, + { tool: LLVM, ver: 17.0.6 }, + ] + build_type: [ Release ] + os: [ windows-2019 ] + std: [ 17 ] + library_type: [ Static ] + include: + - compiler: { tool: mingw } + cxx: g++ + cc: gcc + generator: "MinGW Makefiles" + - compiler: { tool: LLVM } + cxx: clang++ + cc: clang + generator: Ninja + - compiler: { tool: msvc } + cxx: "" + cc: "" + generator: "" + + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + + - name: Create Build Environment + if: matrix.compiler.tool != 'msvc' + shell: bash + run: choco install ${{matrix.compiler.tool}} --version ${{matrix.compiler.ver}} --allow-downgrade -y && choco install ninja && cmake -E make_directory ${{runner.workspace}}/build + + - name: Update Sys Path + if: matrix.compiler.tool == 'mingw' + run: ("${{env.CHOCO_LIB_DIR}}\\mingw\\tools\\install\\mingw64\\bin;" + (Get-Content -Path $env:GITHUB_PATH)) | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 + shell: pwsh + + - name: Configure + env: + CXX: ${{matrix.cxx}} + CC: ${{matrix.cc}} + PARAMETERS: ${{ matrix.compiler.tool == 'msvc' && format('-A x64 -T v{0}', matrix.compiler.ver) || format('-G "{0}"', matrix.generator) }} + run: cmake -B ${{runner.workspace}}/build ${{env.PARAMETERS}} -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} -D_7BIT_CONF_LIBRARY_TYPE=${{matrix.library_type}} -D_7BIT_CONF_BUILD_ALL_TESTS=ON + + - name: Build + run: cmake --build ${{runner.workspace}}/build --config ${{matrix.build_type}} -j + + - name: Test + working-directory: ${{runner.workspace}}/build + run: ctest -C ${{matrix.build_type}} + env: + CTEST_OUTPUT_ON_FAILURE: True diff --git a/Cmake/Setup.cmake b/Cmake/Setup.cmake index c3a0ff6..b3f443a 100644 --- a/Cmake/Setup.cmake +++ b/Cmake/Setup.cmake @@ -1,8 +1,8 @@ include(Functions) -if(NOT CMAKE_BUILD_TYPE) +if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) -endif() +endif () set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) @@ -33,14 +33,14 @@ set(_7BIT_CONF_DETAILS_DIR "${_7BIT_CONF_CONF_DIR}/Details") set(_7BIT_CONF_MAIN_HEADER "${_7BIT_CONF_INCLUDE_DIR}/SevenBit/Conf.hpp") file(GLOB _7BIT_CONF_PUBLIC_HEADERS - "${_7BIT_CONF_CONF_DIR}/*.hpp" - "${_7BIT_CONF_SOURCES_DIR}/*.hpp" + "${_7BIT_CONF_CONF_DIR}/*.hpp" + "${_7BIT_CONF_SOURCES_DIR}/*.hpp" ) file(GLOB _7BIT_CONF_DETAILS_HEADERS "${_7BIT_CONF_DETAILS_DIR}/*.hpp") file(GLOB _7BIT_CONF_IMPL_HEADERS - "${_7BIT_CONF_CONF_DIR}/Impl/*.hpp" - "${_7BIT_CONF_SOURCES_DIR}/Impl/*.hpp" - "${_7BIT_CONF_DETAILS_DIR}/Impl/*.hpp" + "${_7BIT_CONF_CONF_DIR}/Impl/*.hpp" + "${_7BIT_CONF_SOURCES_DIR}/Impl/*.hpp" + "${_7BIT_CONF_DETAILS_DIR}/Impl/*.hpp" ) set(_7BIT_CONF_ALL_HEADERS ${_7BIT_CONF_MAIN_HEADER} ${_7BIT_CONF_PUBLIC_HEADERS} ${_7BIT_CONF_DETAILS_HEADERS} ${_7BIT_CONF_IMPL_HEADERS}) @@ -51,33 +51,33 @@ set_property(CACHE _7BIT_CONF_LIBRARY_TYPE PROPERTY STRINGS Shared Static Header option(_7BIT_CONF_BUILD_PIC "Build position independent code (-fPIC)" OFF) option(_7BIT_CONF_BUILD_EXAMPLES "Build example" OFF) -option(_7BIT_CONF_BUILD_ALL_TESTS "Build tests" OFF) +option(_7BIT_CONF_BUILD_ALL_TESTS "Build all tests" OFF) option(_7BIT_CONF_BUILD_UNIT_TESTS "Build unit tests" OFF) option(_7BIT_CONF_BUILD_INTEGRATION_TESTS "Build integration tests" OFF) option(_7BIT_CONF_BUILD_E2E_TESTS "Build e2e tests" OFF) option(_7BIT_CONF_INSTALL "Installs 7bitConf" OFF) option(_7BIT_CONF_BUILD_SINGLE_HEADER "Builds single header SevenBitConf.hpp" OFF) -if(_7BIT_CONF_BUILD_ALL_TESTS) +if (_7BIT_CONF_BUILD_ALL_TESTS) set(_7BIT_CONF_BUILD_UNIT_TESTS ${_7BIT_CONF_BUILD_ALL_TESTS}) set(_7BIT_CONF_BUILD_INTEGRATION_TESTS ${_7BIT_CONF_BUILD_ALL_TESTS}) set(_7BIT_CONF_BUILD_E2E_TESTS ${_7BIT_CONF_BUILD_ALL_TESTS}) -endif() +endif () -if(_7BIT_CONF_BUILD_PIC) +if (_7BIT_CONF_BUILD_PIC) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -endif() +endif () -if(_7BIT_CONF_LIBRARY_TYPE STREQUAL "Shared") +if (_7BIT_CONF_LIBRARY_TYPE STREQUAL "Shared") set(_7BIT_CONF_BUILD_LIBRARY_TYPE "Shared") set(_7BIT_CONF_SHARED_LIB true) -elseif(_7BIT_CONF_LIBRARY_TYPE STREQUAL "HeaderOnly") +elseif (_7BIT_CONF_LIBRARY_TYPE STREQUAL "HeaderOnly") set(_7BIT_CONF_BUILD_LIBRARY_TYPE "HeaderOnly") set(_7BIT_CONF_HEADER_ONLY_LIB true) -else() +else () set(_7BIT_CONF_BUILD_LIBRARY_TYPE "Static") set(_7BIT_CONF_STATIC_LIB true) -endif() +endif () configure_file(${_7BIT_CONF_CONF_DIR}/CmakeDef.hpp.input ${_7BIT_CONF_CONF_DIR}/CmakeDef.hpp) @@ -85,22 +85,22 @@ set(BYTE_SIZE 8) math(EXPR MEMORY_SIZE "${CMAKE_SIZEOF_VOID_P} * ${BYTE_SIZE}") set(INFOS - "${_7BIT_CONF_LIBRARY} ${_7BIT_CONF_VERSION}" - "Build type: ${CMAKE_BUILD_TYPE}" - "Library type: ${_7BIT_CONF_BUILD_LIBRARY_TYPE}" - "==================================================" - "Cmake version: ${CMAKE_VERSION}" - "Os: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}" - "Architecture: ${CMAKE_SYSTEM_PROCESSOR} ${MEMORY_SIZE}bit" - "CXX compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" - "CXX standard: ${CMAKE_CXX_STANDARD}" - "Generator: ${CMAKE_GENERATOR}" - "==================================================" - "Build unit tests: ${_7BIT_CONF_BUILD_UNIT_TESTS}" - "Build integration tests: ${_7BIT_CONF_BUILD_INTEGRATION_TESTS}" - "Build e2e tests: ${_7BIT_CONF_BUILD_E2E_TESTS}" - "Build examples: ${_7BIT_CONF_BUILD_EXAMPLES}" - "Build single header: ${_7BIT_CONF_BUILD_SINGLE_HEADER}" - "Install project: ${_7BIT_CONF_INSTALL}" + "${_7BIT_CONF_LIBRARY} ${_7BIT_CONF_VERSION}" + "Build type: ${CMAKE_BUILD_TYPE}" + "Library type: ${_7BIT_CONF_BUILD_LIBRARY_TYPE}" + "==================================================" + "Cmake version: ${CMAKE_VERSION}" + "Os: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}" + "Architecture: ${CMAKE_SYSTEM_PROCESSOR} ${MEMORY_SIZE}bit" + "CXX compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" + "CXX standard: ${CMAKE_CXX_STANDARD}" + "Generator: ${CMAKE_GENERATOR}" + "==================================================" + "Build unit tests: ${_7BIT_CONF_BUILD_UNIT_TESTS}" + "Build integration tests: ${_7BIT_CONF_BUILD_INTEGRATION_TESTS}" + "Build e2e tests: ${_7BIT_CONF_BUILD_E2E_TESTS}" + "Build examples: ${_7BIT_CONF_BUILD_EXAMPLES}" + "Build single header: ${_7BIT_CONF_BUILD_SINGLE_HEADER}" + "Install project: ${_7BIT_CONF_INSTALL}" ) printInfo("${INFOS}" = 50 7 0) From be3510ba5ba50b3c3567c7a12c7f7abc2d55a356 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 18:50:35 +0100 Subject: [PATCH 10/31] fix examples --- Examples/CustomConfirugationSource.cpp | 2 -- Examples/CustomSettingParser.cpp | 22 +++++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Examples/CustomConfirugationSource.cpp b/Examples/CustomConfirugationSource.cpp index 5d55650..2b52d9d 100644 --- a/Examples/CustomConfirugationSource.cpp +++ b/Examples/CustomConfirugationSource.cpp @@ -12,8 +12,6 @@ class CustomConfigurationProvider : public IConfigurationProvider public: void load() override { _configuration = {{"mysettingOne", "value1"}, {"mysettingTwo", "value2"}}; } - JsonObject &getConfiguration() override { return _configuration; } - const JsonObject &getConfiguration() const override { return _configuration; } }; diff --git a/Examples/CustomSettingParser.cpp b/Examples/CustomSettingParser.cpp index b463691..4f4618f 100644 --- a/Examples/CustomSettingParser.cpp +++ b/Examples/CustomSettingParser.cpp @@ -10,22 +10,22 @@ struct MyTypeDeserializer final : IDeserializer int main(int argc, char **argv) { - EnvironmentVarsParserConfig envParserConfig; - envParserConfig.keySplitters.clear(); - envParserConfig.settingPrefixes.emplace_back("//"); - envParserConfig.defaultType = "myType"; - envParserConfig.throwOnUnknownType = false; + auto builderFunc = [](CommandLineParserBuilder &builder) { + CommandLineParserConfig parserConfig; + parserConfig.keySplitters.clear(); + parserConfig.optionPrefixes.emplace_back("//"); + parserConfig.defaultType = "myType"; + parserConfig.throwOnUnknownType = false; - ISettingParser::Ptr settingParser = SettingParserBuilder{} // - .useConfig(std::move(envParserConfig)) - .useDefaultValueDeserializers() - .useValueDeserializer("myType", std::make_unique()) - .build(); + builder.useConfig(std::move(parserConfig)) + .useDefaultValueDeserializers() + .useValueDeserializer("myType", std::make_unique()); + }; IConfiguration::Ptr configuration = ConfigurationBuilder{} // .addAppSettings() .addEnvironmentVariables() - .addCommandLine(argc, argv, std::move(settingParser)) + .addCommandLine(argc, argv, builderFunc) .build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; From 02cbe494f1512b62f249135ad9e602a22dc0f72e Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 18:56:01 +0100 Subject: [PATCH 11/31] fix examples --- Source/Source.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Source.cpp b/Source/Source.cpp index fb7785d..43b3cc5 100644 --- a/Source/Source.cpp +++ b/Source/Source.cpp @@ -5,15 +5,15 @@ #include "SevenBit/Conf/Details/Impl/Configuration.hpp" #include "SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp" #include "SevenBit/Conf/Details/Impl/Deserializers.hpp" +#include "SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp" #include "SevenBit/Conf/Details/Impl/JsonObjectExt.hpp" -#include "SevenBit/Conf/Details/Impl/SettingParser.hpp" #include "SevenBit/Conf/Details/Impl/SettingSplitter.hpp" -#include "SevenBit/Conf/Details/Impl/SettingsParser.hpp" -#include "SevenBit/Conf/Details/Impl/Utils.hpp" +#include "SevenBit/Conf/Details/Impl/StringUtils.hpp" #include "SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp" +#include "SevenBit/Conf/Impl/CommandLineParserBuilder.hpp" #include "SevenBit/Conf/Impl/ConfigurationBuilder.hpp" +#include "SevenBit/Conf/Impl/EnvironmentVarsParserBuilder.hpp" #include "SevenBit/Conf/Impl/Exceptions.hpp" -#include "SevenBit/Conf/Impl/SettingParserBuilder.hpp" #include "SevenBit/Conf/Sources/Impl/AppSettingsConfiguration.hpp" #include "SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp" #include "SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp" From 55d3476d361b9b633c079e1359f2e3ddec633949 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 19:52:56 +0100 Subject: [PATCH 12/31] update tests --- CMakeLists.txt | 52 +++++++-------- .../Conf/Details/DefaultDeserializers.hpp | 5 +- .../Details/Impl/DefaultDeserializers.hpp | 10 +++ .../Impl/{JsonObjectExt.hpp => JsonExt.hpp} | 0 .../Conf/Details/Impl/StringUtils.hpp | 5 +- Include/SevenBit/Conf/Details/JsonExt.hpp | 2 +- Source/Source.cpp | 2 +- Tests/Unit/Details/ContainerUtilsTest.cpp | 30 +++++++++ ...{JsonObjectExtTest.cpp => JsonExtTest.cpp} | 26 ++++---- Tests/Unit/Details/RequireTest.cpp | 31 +++++++++ .../{UtilsTest.cpp => StringUtilsTest.cpp} | 64 +++++++++++++------ ...Test.cpp => ValueDeserializersMapTest.cpp} | 22 +++---- 12 files changed, 175 insertions(+), 74 deletions(-) rename Include/SevenBit/Conf/Details/Impl/{JsonObjectExt.hpp => JsonExt.hpp} (100%) create mode 100644 Tests/Unit/Details/ContainerUtilsTest.cpp rename Tests/Unit/Details/{JsonObjectExtTest.cpp => JsonExtTest.cpp} (92%) create mode 100644 Tests/Unit/Details/RequireTest.cpp rename Tests/Unit/Details/{UtilsTest.cpp => StringUtilsTest.cpp} (85%) rename Tests/Unit/Details/{DeserializersTest.cpp => ValueDeserializersMapTest.cpp} (89%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8aa08eb..24c5ad8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(_7BIT_CONF_LIBRARY 7bitConf) set(_7BIT_CONF_VERSION_MAJOR 1) set(_7BIT_CONF_VERSION_MINOR 2) -set(_7BIT_CONF_VERSION_PATCH 1) +set(_7BIT_CONF_VERSION_PATCH 0) set(_7BIT_CONF_VERSION ${_7BIT_CONF_VERSION_MAJOR}.${_7BIT_CONF_VERSION_MINOR}.${_7BIT_CONF_VERSION_PATCH}) @@ -12,9 +12,9 @@ project(${_7BIT_CONF_LIBRARY} LANGUAGES CXX VERSION ${_7BIT_CONF_VERSION}) list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Cmake") -if(NOT CMAKE_CXX_STANDARD) +if (NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) -endif() +endif () set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -26,21 +26,21 @@ include_directories(${_7BIT_CONF_INCLUDE_DIR}) add_subdirectory(Source) -if(_7BIT_CONF_BUILD_UNIT_TESTS OR _7BIT_CONF_BUILD_INTEGRATION_TESTS OR _7BIT_CONF_BUILD_E2E_TESTS) +if (_7BIT_CONF_BUILD_UNIT_TESTS OR _7BIT_CONF_BUILD_INTEGRATION_TESTS OR _7BIT_CONF_BUILD_E2E_TESTS) enable_testing() add_subdirectory(Tests) -endif() +endif () -if(_7BIT_CONF_BUILD_EXAMPLES) +if (_7BIT_CONF_BUILD_EXAMPLES) add_subdirectory(Examples) -endif() +endif () -if(_7BIT_CONF_BUILD_SINGLE_HEADER) +if (_7BIT_CONF_BUILD_SINGLE_HEADER) add_subdirectory(SingleHeader) -endif() +endif () -if(_7BIT_CONF_INSTALL) +if (_7BIT_CONF_INSTALL) set(PROJECT_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/Cmake/7bitConfConfig.cmake.in) set(PROJECT_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/7bitConfConfig.cmake) set(CONFIG_TARGETS_FILE 7bitConfConfigTargets.cmake) @@ -50,29 +50,29 @@ if(_7BIT_CONF_INSTALL) install(DIRECTORY ${_7BIT_CONF_INCLUDE_DIR}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( - TARGETS 7bitConf - EXPORT 7bitConf - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + TARGETS 7bitConf + EXPORT 7bitConf + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) export( - TARGETS 7bitConf - NAMESPACE 7bitConf:: - FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_TARGETS_FILE}) + TARGETS 7bitConf + NAMESPACE 7bitConf:: + FILE ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_TARGETS_FILE}) install( - EXPORT 7bitConf - EXPORT 7bitConf - DESTINATION ${EXPORT_DEST_DIR} - NAMESPACE 7bitConf:: - NAMESPACE 7bitConf:: - FILE ${CONFIG_TARGETS_FILE}) + EXPORT 7bitConf + EXPORT 7bitConf + DESTINATION ${EXPORT_DEST_DIR} + NAMESPACE 7bitConf:: + NAMESPACE 7bitConf:: + FILE ${CONFIG_TARGETS_FILE}) include(CMakePackageConfigHelpers) configure_package_config_file(${PROJECT_CONFIG_IN} ${PROJECT_CONFIG_OUT} - INSTALL_DESTINATION ${EXPORT_DEST_DIR}) + INSTALL_DESTINATION ${EXPORT_DEST_DIR}) write_basic_package_version_file(${VERSIONS_CONFIG_FILE} COMPATIBILITY SameMajorVersion) @@ -81,4 +81,4 @@ if(_7BIT_CONF_INSTALL) export(PACKAGE 7bitConf) include(CPack) -endif() +endif () diff --git a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp index ee037dd..3e69ad5 100644 --- a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp +++ b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp @@ -3,14 +3,17 @@ #include "SevenBit/Conf/LibraryConfig.hpp" #include "SevenBit/Conf/Details/Deserializers.hpp" +#include "SevenBit/Conf/Details/ValueDeserializersMap.hpp" namespace sb::cf::details { struct DefaultDeserializers { static void add(std::vector> &deserializers); + + static void add(ValueDeserializersMap &deserializersMap); }; -} // namespace sb::cf +} // namespace sb::cf::details #ifdef _7BIT_CONF_ADD_IMPL #include "SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp" diff --git a/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp index 29223c9..5d20f3e 100644 --- a/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp +++ b/Include/SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp @@ -15,4 +15,14 @@ namespace sb::cf::details deserializers.emplace_back("null", std::make_unique()); } + INLINE void DefaultDeserializers::add(ValueDeserializersMap &deserializersMap) + { + std::vector> deserializers; + add(deserializers); + for (auto &[type, deserializer] : deserializers) + { + deserializersMap.set(type, std::move(deserializer)); + } + } + } // namespace sb::cf::details diff --git a/Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp b/Include/SevenBit/Conf/Details/Impl/JsonExt.hpp similarity index 100% rename from Include/SevenBit/Conf/Details/Impl/JsonObjectExt.hpp rename to Include/SevenBit/Conf/Details/Impl/JsonExt.hpp diff --git a/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp b/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp index ba07fb6..4def691 100644 --- a/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp +++ b/Include/SevenBit/Conf/Details/Impl/StringUtils.hpp @@ -89,7 +89,10 @@ namespace sb::cf::details return it - str.begin(); } - INLINE bool StringUtils::isWhiteSpace(std::string_view str) { return startsWithWhiteSpace(str) == str.size(); } + INLINE bool StringUtils::isWhiteSpace(std::string_view str) + { + return !str.empty() && startsWithWhiteSpace(str) == str.size(); + } INLINE std::vector StringUtils::split(std::string_view str, std::string_view separator) { diff --git a/Include/SevenBit/Conf/Details/JsonExt.hpp b/Include/SevenBit/Conf/Details/JsonExt.hpp index 83e6b90..cfe4c66 100644 --- a/Include/SevenBit/Conf/Details/JsonExt.hpp +++ b/Include/SevenBit/Conf/Details/JsonExt.hpp @@ -75,5 +75,5 @@ namespace sb::cf::details } // namespace sb::cf::details #ifdef _7BIT_CONF_ADD_IMPL -#include "SevenBit/Conf/Details/Impl/JsonObjectExt.hpp" +#include "SevenBit/Conf/Details/Impl/JsonExt.hpp" #endif diff --git a/Source/Source.cpp b/Source/Source.cpp index 43b3cc5..c23ee7b 100644 --- a/Source/Source.cpp +++ b/Source/Source.cpp @@ -6,7 +6,7 @@ #include "SevenBit/Conf/Details/Impl/DefaultDeserializers.hpp" #include "SevenBit/Conf/Details/Impl/Deserializers.hpp" #include "SevenBit/Conf/Details/Impl/EnvironmentVarsParser.hpp" -#include "SevenBit/Conf/Details/Impl/JsonObjectExt.hpp" +#include "SevenBit/Conf/Details/Impl/JsonExt.hpp" #include "SevenBit/Conf/Details/Impl/SettingSplitter.hpp" #include "SevenBit/Conf/Details/Impl/StringUtils.hpp" #include "SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp" diff --git a/Tests/Unit/Details/ContainerUtilsTest.cpp b/Tests/Unit/Details/ContainerUtilsTest.cpp new file mode 100644 index 0000000..40b14ea --- /dev/null +++ b/Tests/Unit/Details/ContainerUtilsTest.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include "SevenBit/Conf/Details/ContainerUtils.hpp" + +class ContainerUtilsTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + ContainerUtilsTest() {} + + void SetUp() override {} + + void TearDown() override {} + + ~ContainerUtilsTest() {} + + static void TearDownTestSuite() {} +}; + +TEST_F(ContainerUtilsTest, ShouldEraseElements) +{ + std::vector vec = {1, 2, 3, 4, 5, 6}; + + auto removed = sb::cf::details::ContainerUtils::eraseIf(vec, [](int val) { return val % 2; }); + + EXPECT_EQ(removed, 3); + EXPECT_EQ(vec, (std::vector{2, 4, 6})); +} diff --git a/Tests/Unit/Details/JsonObjectExtTest.cpp b/Tests/Unit/Details/JsonExtTest.cpp similarity index 92% rename from Tests/Unit/Details/JsonObjectExtTest.cpp rename to Tests/Unit/Details/JsonExtTest.cpp index fef549e..01b8c82 100644 --- a/Tests/Unit/Details/JsonObjectExtTest.cpp +++ b/Tests/Unit/Details/JsonExtTest.cpp @@ -7,12 +7,12 @@ using namespace sb::cf::json; -class JsonObjectExtTest : public testing::Test +class JsonExtTest : public testing::Test { protected: static void TearUpTestSuite() {} - JsonObjectExtTest() {} + JsonExtTest() {} void SetUp() override {} @@ -28,7 +28,7 @@ Params FindData{ {"inner", true, -123}, {"nonExisting", false, tao::json::null}, }; -PARAMS_TEST(JsonObjectExtTest, ShouldFing, FindData) +PARAMS_TEST(JsonExtTest, ShouldFing, FindData) { sb::cf::JsonValue json = { {"str", "hello"}, {"number", 123}, {"array", sb::cf::JsonArray{{{"key", "value"}}}}, {"inner", -123}}; @@ -59,7 +59,7 @@ Params DeepFindData{ {"inner::inner:str", false, tao::json::null}, }; -PARAMS_TEST(JsonObjectExtTest, ShouldDeepFing, DeepFindData) +PARAMS_TEST(JsonExtTest, ShouldDeepFing, DeepFindData) { sb::cf::JsonValue json = {{"str", "hello"}, {"number", 123}, @@ -82,7 +82,7 @@ PARAMS_TEST(JsonObjectExtTest, ShouldDeepFing, DeepFindData) } } -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverride) +TEST_F(JsonExtTest, ShouldDeepGetOrOverride) { sb::cf::JsonObject json = {{"str", "hello"}, {"number", 123}, @@ -114,7 +114,7 @@ TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverride) EXPECT_EQ(json, expectedJson); } -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideArrayElement) +TEST_F(JsonExtTest, ShouldDeepGetOrOverrideArrayElement) { sb::cf::JsonObject json = {{"str", "hello"}}; @@ -129,7 +129,7 @@ TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideArrayElement) EXPECT_EQ(json, expected); } -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideExistingArrayElement) +TEST_F(JsonExtTest, ShouldDeepGetOrOverrideExistingArrayElement) { sb::cf::JsonObject json = {{"str", "hello"}, {"array", sb::cf::JsonArray{1234, {{"second", "element"}}}}}; @@ -143,7 +143,7 @@ TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideExistingArrayElement) EXPECT_EQ(json, expected); } -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideWrongArrayElement) +TEST_F(JsonExtTest, ShouldDeepGetOrOverrideWrongArrayElement) { sb::cf::JsonObject json = {{"str", "hello"}}; @@ -154,7 +154,7 @@ TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideWrongArrayElement) EXPECT_EQ(json, expected); } -TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideDestroy) +TEST_F(JsonExtTest, ShouldDeepGetOrOverrideDestroy) { sb::cf::JsonObject json = {{"str", "hello"}, {"number", 123}, @@ -183,14 +183,14 @@ TEST_F(JsonObjectExtTest, ShouldDeepGetOrOverrideDestroy) EXPECT_EQ(json, expected); } -TEST_F(JsonObjectExtTest, ShouldFaildDeepGetOrOverrideEmpty) +TEST_F(JsonExtTest, ShouldFaildDeepGetOrOverrideEmpty) { sb::cf::JsonObject json = {{"str", "hello"}}; EXPECT_ANY_THROW(sb::cf::details::JsonExt::deepGetOrOverride(json, std::vector{})); } -TEST_F(JsonObjectExtTest, SouldDeepMergeEmptyJsonValue) +TEST_F(JsonExtTest, SouldDeepMergeEmptyJsonValue) { sb::cf::JsonValue json; @@ -203,7 +203,7 @@ TEST_F(JsonObjectExtTest, SouldDeepMergeEmptyJsonValue) EXPECT_EQ(json, expected); } -TEST_F(JsonObjectExtTest, SouldDeepMergeJsonValue) +TEST_F(JsonExtTest, SouldDeepMergeJsonValue) { sb::cf::JsonValue json = {{"str", "hello"}, @@ -246,7 +246,7 @@ TEST_F(JsonObjectExtTest, SouldDeepMergeJsonValue) EXPECT_EQ(json, expectedJson); } -TEST_F(JsonObjectExtTest, SouldDeepMergeJsonArray) +TEST_F(JsonExtTest, SouldDeepMergeJsonArray) { sb::cf::JsonArray json{{{"str", "hello"}}, diff --git a/Tests/Unit/Details/RequireTest.cpp b/Tests/Unit/Details/RequireTest.cpp new file mode 100644 index 0000000..ac7177c --- /dev/null +++ b/Tests/Unit/Details/RequireTest.cpp @@ -0,0 +1,31 @@ +#include +#include + +#include "SevenBit/Conf/Details/Require.hpp" + +class RequireTest : public testing::Test +{ + protected: + static void TearUpTestSuite() {} + + RequireTest() {} + + void SetUp() override {} + + void TearDown() override {} + + ~RequireTest() {} + + static void TearDownTestSuite() {} +}; + +TEST_F(RequireTest, ShouldRequireNotNull) +{ + int test = 123; + EXPECT_THROW(sb::cf::details::Require::notNull(nullptr), sb::cf::NullPointerException); + EXPECT_THROW(sb::cf::details::Require::notNull(std::unique_ptr{}), sb::cf::NullPointerException); + EXPECT_THROW(sb::cf::details::Require::notNull(std::shared_ptr{}), sb::cf::NullPointerException); + EXPECT_NO_THROW(sb::cf::details::Require::notNull(&test)); + EXPECT_NO_THROW(sb::cf::details::Require::notNull(std::shared_ptr{new int})); + EXPECT_NO_THROW(sb::cf::details::Require::notNull(std::unique_ptr{new int})); +} diff --git a/Tests/Unit/Details/UtilsTest.cpp b/Tests/Unit/Details/StringUtilsTest.cpp similarity index 85% rename from Tests/Unit/Details/UtilsTest.cpp rename to Tests/Unit/Details/StringUtilsTest.cpp index a7e17de..f47fe7f 100644 --- a/Tests/Unit/Details/UtilsTest.cpp +++ b/Tests/Unit/Details/StringUtilsTest.cpp @@ -4,12 +4,12 @@ #include "../../../Include/SevenBit/Conf/Details/StringUtils.hpp" #include "../../Helpers/Utilities/ParamsTest.hpp" -class UtilsTest : public testing::Test +class StringUtilsTest : public testing::Test { protected: static void TearUpTestSuite() {} - UtilsTest() {} + StringUtilsTest() {} void SetUp() override {} @@ -23,7 +23,7 @@ Params CheckNumberStringsData{ {"", false}, {"alk1", false}, {"1223-", false}, {"1223#", false}, {"1223+=", false}, {"1223.123", false}, {"1223.123", false}, }; -PARAMS_TEST(UtilsTest, ShouldCheckNumberStrings, CheckNumberStringsData) +PARAMS_TEST(StringUtilsTest, ShouldCheckNumberStrings, CheckNumberStringsData) { auto &[string, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::isNumber(string), expected); @@ -43,7 +43,7 @@ Params IgnoreCaseLessData{ {"ab", "ab\n", true}, {"1222", "12", false}, }; -PARAMS_TEST(UtilsTest, ShouldIgnoreCaseLessCompareStrings, IgnoreCaseLessData) +PARAMS_TEST(StringUtilsTest, ShouldIgnoreCaseLessCompareStrings, IgnoreCaseLessData) { auto &[string, search, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::ignoreCaseLess(string, search), expected); @@ -63,7 +63,7 @@ Params IgnoreCaseEqualsData{ {"ab", "ab\n", false}, {"1222", "12", false}, }; -PARAMS_TEST(UtilsTest, ShouldIgnoreCaseCompareStrings, IgnoreCaseEqualsData) +PARAMS_TEST(StringUtilsTest, ShouldIgnoreCaseCompareStrings, IgnoreCaseEqualsData) { auto &[string, search, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::ignoreCaseEqual(string, search), expected); @@ -78,7 +78,7 @@ Params ContainsAtData{ {"", 0, "abcded", false}, {"BA", 0, "ab", false}, {"ab", 0, "ab\n", false}, {"1222", 0, "12", true}, }; -PARAMS_TEST(UtilsTest, ShouldContainsAt, ContainsAtData) +PARAMS_TEST(StringUtilsTest, ShouldContainsAt, ContainsAtData) { auto &[string, index, search, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::containsAt(string, index, search), expected); @@ -104,7 +104,7 @@ Params, std::optional ContainsAtFromEndData{ {"123@#", 2, "@", false}, {"abcdef", 5, "abcdef", true}, {"", 0, "abcded", false}, {"BA", 2, "ab", false}, {"ab", 2, "ab\n", false}, {"1222", 1, "12", true}, }; -PARAMS_TEST(UtilsTest, ShouldContainsAtFromEnd, ContainsAtFromEndData) +PARAMS_TEST(StringUtilsTest, ShouldContainsAtFromEnd, ContainsAtFromEndData) { auto &[string, index, search, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::containsAtFromEnd(string, index, search), expected); @@ -147,7 +147,7 @@ Params, std::optional StartsWithData{ {"123", "890", false}, {"123", "1245", false}, {"1234567", "234", false}, {"", "234", false}, {"123", "234", false}, }; -PARAMS_TEST(UtilsTest, ShouldStartsWith, StartsWithData) +PARAMS_TEST(StringUtilsTest, ShouldStartsWith, StartsWithData) { auto &[string, search, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::startsWith(string, search), expected); } +Params StartsWithWhiteSpaceData{ + {"", 0}, + {"asddwq", 0}, + {" 1234567", 1}, + {" 1234567", 2}, + {"\n\t\nasdwd", 3}, + {"\n \nasdwd", 4}, + {" \n 1234567", 6}, + {" \n1234567", 6}, + {" \t 1234567", 6}, + {" asd", 6}, +}; +PARAMS_TEST(StringUtilsTest, ShouldStartsWithWhiteSpace, StartsWithWhiteSpaceData) +{ + auto &[string, expectedCnt] = GetParam(); + EXPECT_EQ(sb::cf::details::StringUtils::startsWithWhiteSpace(string), expectedCnt); +} + +Params CheckWhiteSpaceData{ + {"", false}, {"asddwq", false}, {" ", true}, {"\n\t\n", true}, {"\n \nasdw", false}, + {" \n ", true}, {" \n asdadd", false}, {" \n", true}, {" \t ", true}, {" asd", false}, +}; +PARAMS_TEST(StringUtilsTest, ShouldCheckWhiteSpace, CheckWhiteSpaceData) +{ + auto &[string, expected] = GetParam(); + EXPECT_EQ(sb::cf::details::StringUtils::isWhiteSpace(string), expected); +} + Params> SplitStrData{ {"", "/", {""}}, {"", "", {""}}, @@ -178,7 +206,7 @@ Params> SplitStrData{ {"first__sec__for__5__6__", "__", {"first", "sec", "for", "5", "6", ""}}, {":::", ":", {"", "", "", ""}}, }; -PARAMS_TEST(UtilsTest, ShouldSplitString, SplitStrData) +PARAMS_TEST(StringUtilsTest, ShouldSplitString, SplitStrData) { auto &[string, delim, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::split(string, delim), expected); @@ -201,7 +229,7 @@ Params, std::vector {"first:sec:for:5:6:", {":"}, {"first", "sec", "for", "5", "6", ""}}, {":::", {":"}, {"", "", "", ""}}, }; -PARAMS_TEST(UtilsTest, ShouldSplitStringMulti, SplitStrMultiData) +PARAMS_TEST(StringUtilsTest, ShouldSplitStringMulti, SplitStrMultiData) { auto &[string, delims, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::split(string, delims), expected); @@ -222,7 +250,7 @@ Params, std::optional{"first", "sec:for:5:6"}}, {"first__sec__for__5__6__", {"__"}, std::pair{"first", "sec__for__5__6__"}}, }; -PARAMS_TEST(UtilsTest, ShouldBreakString, BreakStrData) +PARAMS_TEST(StringUtilsTest, ShouldBreakString, BreakStrData) { auto &[string, delim, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::tryBreak(string, delim), expected); @@ -243,7 +271,7 @@ Params, std::optional{"first:sec:for:5", "6"}}, {"first__sec__for__5__6", {"__"}, std::pair{"first__sec__for__5", "6"}}, }; -PARAMS_TEST(UtilsTest, ShouldBreakFromEndString, BreakFromEndStrData) +PARAMS_TEST(StringUtilsTest, ShouldBreakFromEndString, BreakFromEndStrData) { auto &[string, delim, expected] = GetParam(); auto res = sb::cf::details::StringUtils::tryBreakFromEnd(string, delim); @@ -262,7 +290,7 @@ Params, std::string, std::string> JoinStrData{ {{"first", "sec", "for:5:6:"}, ":", "first:sec:for:5:6:"}, {{"first", "sec", "for", "5", "6"}, ":", "first:sec:for:5:6"}, }; -PARAMS_TEST(UtilsTest, ShouldJoinStrings, JoinStrData) +PARAMS_TEST(StringUtilsTest, ShouldJoinStrings, JoinStrData) { auto &[strings, delim, expected] = GetParam(); EXPECT_EQ(sb::cf::details::StringUtils::join(strings, delim), expected); @@ -272,7 +300,7 @@ Params> ConvertToNumberIntData{ {"123", true, {true, 123}}, {"-123", true, {true, -123}}, {"00123", true, {true, 123}}, {"123 23", false, {true, 123}}, {"123.23", false, {true, 123}}, {"123adawadwa", false, {true, 123}}, }; -PARAMS_TEST(UtilsTest, ShouldConvertToIntNumber, ConvertToNumberIntData) +PARAMS_TEST(StringUtilsTest, ShouldConvertToIntNumber, ConvertToNumberIntData) { auto &[string, full, expected] = GetParam(); auto [success, result] = sb::cf::details::StringUtils::tryConvertTo(string, full); @@ -289,7 +317,7 @@ Params> ConvertToNumberDoubleDat {"00123.2", true, {true, 123.2}}, {" 123.23asdw", false, {true, 123.23}}, {"123.23", false, {true, 123.23}}, {"123.1adawadwa", false, {true, 123.1}}, }; -PARAMS_TEST(UtilsTest, ShouldConvertToDoubleNumber, ConvertToNumberDoubleData) +PARAMS_TEST(StringUtilsTest, ShouldConvertToDoubleNumber, ConvertToNumberDoubleData) { auto &[string, full, expected] = GetParam(); auto [success, result] = sb::cf::details::StringUtils::tryConvertTo(string, full); @@ -307,7 +335,7 @@ Params> ConvertToBoolData{ {"0 asdad", false, {true, false}}, {"12 asdad", false, {true, true}}, {" 12", true, {true, true}}, {"ttrue", true, {false, true}}, }; -PARAMS_TEST(UtilsTest, ShouldConvertToBool, ConvertToBoolData) +PARAMS_TEST(StringUtilsTest, ShouldConvertToBool, ConvertToBoolData) { auto &[string, full, expected] = GetParam(); auto [success, result] = sb::cf::details::StringUtils::tryConvertTo(string, full); diff --git a/Tests/Unit/Details/DeserializersTest.cpp b/Tests/Unit/Details/ValueDeserializersMapTest.cpp similarity index 89% rename from Tests/Unit/Details/DeserializersTest.cpp rename to Tests/Unit/Details/ValueDeserializersMapTest.cpp index a093b63..5edae23 100644 --- a/Tests/Unit/Details/DeserializersTest.cpp +++ b/Tests/Unit/Details/ValueDeserializersMapTest.cpp @@ -9,12 +9,14 @@ #include "../../../Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp" #include "../../Helpers/Utilities/ParamsTest.hpp" -class DeserializersTest : public testing::Test +#include + +class ValueDeserializersMapTest : public testing::Test { protected: static void TearUpTestSuite() {} - DeserializersTest() {} + ValueDeserializersMapTest() {} void SetUp() override {} @@ -27,13 +29,7 @@ sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap(std::string_v bool throwOnUnknownType = true) { sb::cf::details::ValueDeserializersMap deserializers{defaultType, throwOnUnknownType}; - deserializers.set("string", std::make_unique()); - deserializers.set("bool", std::make_unique()); - deserializers.set("int", std::make_unique()); - deserializers.set("double", std::make_unique()); - deserializers.set("uint", std::make_unique()); - deserializers.set("json", std::make_unique()); - deserializers.set("null", std::make_unique()); + sb::cf::details::DefaultDeserializers::add(deserializers); return std::move(deserializers); } @@ -154,7 +150,7 @@ static Params, sb::cf::JsonVal {"qwdv", "", std::string{""}}, {"das", std::nullopt, std::string{""}}, }; -PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) +PARAMS_TEST(ValueDeserializersMapTest, ShouldDeserializeValue, DeserializeData) { const auto &[type, value, expected] = GetParam(); auto deserializers = makeDefaultDeserializersMap("string", false); @@ -163,7 +159,7 @@ PARAMS_TEST(DeserializersTest, ShouldDeserializeValue, DeserializeData) EXPECT_EQ(deserializer.deserialize(value), expected); } -TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) +TEST_F(ValueDeserializersMapTest, ShouldDeserializeEmptyJsonOption) { auto deserializers = makeDefaultDeserializersMap(); @@ -171,7 +167,7 @@ TEST_F(DeserializersTest, ShouldDeserializeEmptyJsonOption) EXPECT_EQ((deserializer.deserialize(std::nullopt)), (sb::cf::JsonValue{})); } -TEST_F(DeserializersTest, ShouldNotFoundDeserializer) +TEST_F(ValueDeserializersMapTest, ShouldNotFoundDeserializer) { auto deserializers = makeDefaultDeserializersMap(); @@ -235,7 +231,7 @@ static Params> FailDeserialize {"json", ""}, {"json", "\n"}, }; -PARAMS_TEST(DeserializersTest, ShouldFailDeserialize, FailDeserializeValues) +PARAMS_TEST(ValueDeserializersMapTest, ShouldFailDeserialize, FailDeserializeValues) { const auto &[type, value] = GetParam(); auto deserializers = makeDefaultDeserializersMap(); From aff47e24b463c4722a0bd4f929bb4df2758a462f Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 21:11:11 +0100 Subject: [PATCH 13/31] update tests and readme --- Examples/Basic.cpp | 18 +-- Examples/CustomConfirugationSource.cpp | 5 +- Examples/CustomSettingParser.cpp | 17 +-- Examples/SettingParserConfig.cpp | 12 +- Include/SevenBit/Conf/CmakeDef.hpp | 2 +- .../Conf/Details/DefaultDeserializers.hpp | 2 +- .../Details/Impl/ValueDeserializersMap.hpp | 3 +- .../SevenBit/Conf/Details/SettingSplitter.hpp | 1 - .../Conf/Details/ValueDeserializersMap.hpp | 2 +- .../Sources/Impl/CommandLineConfiguration.hpp | 2 +- README.md | 52 ++++---- Tests/CMakeLists.txt | 2 +- Tests/{EndToEnd => E2E}/CMakeLists.txt | 2 +- Tests/E2E/echo.cpp | 14 +++ Tests/{EndToEnd => E2E}/echoTestData.json | 36 +++--- Tests/E2E/echo_test.py | 60 +++++++++ Tests/EndToEnd/echo.cpp | 14 --- Tests/EndToEnd/echo_test.py | 59 --------- Tests/Helpers/Classes/CustomConfigSource.hpp | 2 +- Tests/Helpers/Files/Directory/settingTwo.json | 8 +- Tests/Integration/ConfigurationTest.cpp | 76 ++++++------ Tests/Unit/CommandLineParserBuilderTest.cpp | 18 +-- Tests/Unit/ConfigurationBuilderTest.cpp | 2 +- Tests/Unit/Details/ContainerUtilsTest.cpp | 5 +- Tests/Unit/Details/JsonExtTest.cpp | 94 +++++++------- Tests/Unit/Details/RequireTest.cpp | 3 +- Tests/Unit/Details/SettingSplitterTest.cpp | 10 +- Tests/Unit/Details/StringUtilsTest.cpp | 1 - .../Details/ValueDeserializersMapTest.cpp | 12 +- .../Unit/EnvironmentVarsParserBuilderTest.cpp | 14 +-- Tests/Unit/ObjectHolderTest.cpp | 4 +- .../Sources/AppSettingsConfigurationTest.cpp | 18 +-- .../Unit/Sources/ChainedConfigurationTest.cpp | 24 ++-- .../Sources/CommandLineConfigurationTest.cpp | 117 +++++++++--------- .../EnvironmentVarsConfigurationTest.cpp | 46 +++---- .../Sources/InMemoryConfigurationTest.cpp | 5 +- Tests/Unit/Sources/JsonConfigurationTest.cpp | 4 +- .../Sources/JsonFileConfigurationTest.cpp | 14 +-- .../Sources/JsonStreamConfigurationTest.cpp | 12 +- .../Sources/KeyPerFileConfigurationTest.cpp | 44 +++---- Tests/Unit/Sources/MapConfigutationTest.cpp | 16 +-- Tests/Unit/TestTemplate.cpp | 3 +- 42 files changed, 431 insertions(+), 424 deletions(-) rename Tests/{EndToEnd => E2E}/CMakeLists.txt (88%) create mode 100644 Tests/E2E/echo.cpp rename Tests/{EndToEnd => E2E}/echoTestData.json (72%) create mode 100644 Tests/E2E/echo_test.py delete mode 100644 Tests/EndToEnd/echo.cpp delete mode 100644 Tests/EndToEnd/echo_test.py diff --git a/Examples/Basic.cpp b/Examples/Basic.cpp index 930f343..25654aa 100644 --- a/Examples/Basic.cpp +++ b/Examples/Basic.cpp @@ -3,17 +3,17 @@ using namespace sb::cf; -int main(int argc, char **argv) +int main(const int argc, char **argv) { - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addAppSettings() - .addEnvironmentVariables() - .addCommandLine(argc, argv) - .build(); + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addEnvironmentVariables() + .addCommandLine(argc, argv) + .build(); - std::string value = configuration->at("MySetting").get_string(); - std::string defaultLogLevel = configuration->deepAt("Logging:LogLevel:Default").get_string(); - std::uint64_t secondArrayElement = configuration->deepAt("Array:1").get_unsigned(); + const std::string value = configuration->at("MySetting").get_string(); + const std::string defaultLogLevel = configuration->deepAt("Logging:LogLevel:Default").get_string(); + const std::uint64_t secondArrayElement = configuration->deepAt("Array:1").get_unsigned(); std::cout << "MySetting: " << value << std::endl; std::cout << "Default LogLevel: " << defaultLogLevel << std::endl; diff --git a/Examples/CustomConfirugationSource.cpp b/Examples/CustomConfirugationSource.cpp index 2b52d9d..1edf492 100644 --- a/Examples/CustomConfirugationSource.cpp +++ b/Examples/CustomConfirugationSource.cpp @@ -6,13 +6,12 @@ using namespace sb::cf; class CustomConfigurationProvider : public IConfigurationProvider { - private: JsonObject _configuration; public: void load() override { _configuration = {{"mysettingOne", "value1"}, {"mysettingTwo", "value2"}}; } - const JsonObject &getConfiguration() const override { return _configuration; } + [[nodiscard]] const JsonObject &getConfiguration() const override { return _configuration; } }; class CustomConfigurationSource : public IConfigurationSource @@ -26,7 +25,7 @@ class CustomConfigurationSource : public IConfigurationSource int main(int argc, char **argv) { - IConfiguration::Ptr configuration = + const IConfiguration::Ptr configuration = ConfigurationBuilder{}.add(std::make_unique()).build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; diff --git a/Examples/CustomSettingParser.cpp b/Examples/CustomSettingParser.cpp index 4f4618f..068f2bd 100644 --- a/Examples/CustomSettingParser.cpp +++ b/Examples/CustomSettingParser.cpp @@ -5,10 +5,13 @@ using namespace sb::cf; struct MyTypeDeserializer final : IDeserializer { - JsonValue deserialize(std::optional value) const final { return value ? value : "emptyValue"; } + [[nodiscard]] JsonValue deserialize(std::optional value) const override + { + return value ? value : "emptyValue"; + } }; -int main(int argc, char **argv) +int main(const int argc, char **argv) { auto builderFunc = [](CommandLineParserBuilder &builder) { CommandLineParserConfig parserConfig; @@ -22,11 +25,11 @@ int main(int argc, char **argv) .useValueDeserializer("myType", std::make_unique()); }; - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addAppSettings() - .addEnvironmentVariables() - .addCommandLine(argc, argv, builderFunc) - .build(); + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addEnvironmentVariables() + .addCommandLine(argc, argv, builderFunc) + .build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; diff --git a/Examples/SettingParserConfig.cpp b/Examples/SettingParserConfig.cpp index 39f74cf..4d3240a 100644 --- a/Examples/SettingParserConfig.cpp +++ b/Examples/SettingParserConfig.cpp @@ -3,17 +3,17 @@ using namespace sb::cf; -int main(int argc, char **argv) +int main(const int argc, char **argv) { EnvironmentVarsParserConfig envParserConfig; envParserConfig.keySplitters.clear(); envParserConfig.typeMarkers.clear(); - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addAppSettings() - .addEnvironmentVariables("", std::move(envParserConfig)) - .addCommandLine(argc, argv) - .build(); + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addEnvironmentVariables("", std::move(envParserConfig)) + .addCommandLine(argc, argv) + .build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; diff --git a/Include/SevenBit/Conf/CmakeDef.hpp b/Include/SevenBit/Conf/CmakeDef.hpp index a3b0b8d..4e8f8d7 100644 --- a/Include/SevenBit/Conf/CmakeDef.hpp +++ b/Include/SevenBit/Conf/CmakeDef.hpp @@ -14,4 +14,4 @@ #define _7BIT_CONF_VERSION_MAJOR 1 #define _7BIT_CONF_VERSION_MINOR 2 -#define _7BIT_CONF_VERSION_PATCH 1 +/* #undef _7BIT_CONF_VERSION_PATCH */ diff --git a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp index 3e69ad5..74b17ef 100644 --- a/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp +++ b/Include/SevenBit/Conf/Details/DefaultDeserializers.hpp @@ -7,7 +7,7 @@ namespace sb::cf::details { - struct DefaultDeserializers + struct EXPORT DefaultDeserializers { static void add(std::vector> &deserializers); diff --git a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp index d270e5b..81b3e4e 100644 --- a/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/Impl/ValueDeserializersMap.hpp @@ -6,7 +6,8 @@ namespace sb::cf::details { INLINE ValueDeserializersMap::ValueDeserializersMap(const std::string_view defaultType, - const bool throwOnUnknownType, Deserializers valueDeserializers) + const bool throwOnUnknownType, + Deserializers &&valueDeserializers) : _defaultType(defaultType), _throwOnUnknownType(throwOnUnknownType) { for (auto &[type, deserializer] : valueDeserializers) diff --git a/Include/SevenBit/Conf/Details/SettingSplitter.hpp b/Include/SevenBit/Conf/Details/SettingSplitter.hpp index aa552ff..b6c47aa 100644 --- a/Include/SevenBit/Conf/Details/SettingSplitter.hpp +++ b/Include/SevenBit/Conf/Details/SettingSplitter.hpp @@ -14,7 +14,6 @@ namespace sb::cf::details class EXPORT SettingSplitter : public ISettingSplitter { - private: const std::vector _settingSplitters; const std::vector _typeMarkers; const std::vector _keySplitters; diff --git a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp index fc615d1..e775d8a 100644 --- a/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp +++ b/Include/SevenBit/Conf/Details/ValueDeserializersMap.hpp @@ -37,7 +37,7 @@ namespace sb::cf::details using Ptr = std::unique_ptr; explicit ValueDeserializersMap(std::string_view defaultType, bool throwOnUnknownType = true, - Deserializers valueDeserializers = {}); + Deserializers &&valueDeserializers = {}); ValueDeserializersMap(const ValueDeserializersMap &) = delete; ValueDeserializersMap(ValueDeserializersMap &&) noexcept = default; diff --git a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp index 2b2af05..05e766a 100644 --- a/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/CommandLineConfiguration.hpp @@ -12,7 +12,7 @@ namespace sb::cf details::Require::notNull(_parser); } - INLINE CommandLineConfigurationSource::SPtr CommandLineConfigurationSource::create(int argc, + INLINE CommandLineConfigurationSource::SPtr CommandLineConfigurationSource::create(const int argc, const char *const *argv, ISettingsParser::Ptr parser) { diff --git a/README.md b/README.md index cc6c58b..0b3430f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -[![CI](https://github.com/7bitcoder/7bitConf/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/CI.yml) ![Conan Center](https://img.shields.io/conan/v/7bitconf) +[![CI](https://github.com/7bitcoder/7bitConf/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/CI.yml) +[![DevCI](https://github.com/7bitcoder/7bitConf/actions/workflows/DevCI.yml/badge.svg?branch=dev)](https://github.com/7bitcoder/7bitConf/actions/workflows/DevCI.yml) +[![Windows](https://github.com/7bitcoder/7bitConf/actions/workflows/Windows.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/Windows.yml) +[![Linux](https://github.com/7bitcoder/7bitConf/actions/workflows/Linux.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/Linux.yml) +[![MacOs](https://github.com/7bitcoder/7bitConf/actions/workflows/MacOs.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/MacOs.yml) +[![Conan Center](https://img.shields.io/conan/v/7bitconf)](https://conan.io/center/recipes/7bitconf)
@@ -131,17 +136,17 @@ Create the appsettings.json file in the compiled executable directory: using namespace sb::cf; -int main(int argc, char **argv) +int main(const int argc, char **argv) { - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addAppSettings() - .addEnvironmentVariables() - .addCommandLine(argc, argv) - .build(); + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addEnvironmentVariables() + .addCommandLine(argc, argv) + .build(); - std::string value = configuration->at("MySetting").get_string(); - std::string defaultLogLevel = configuration->deepAt("Logging:LogLevel:Default").get_string(); - std::uint64_t secondArrayElement = configuration->deepAt("Array:1").get_unsigned(); + const std::string value = configuration->at("MySetting").get_string(); + const std::string defaultLogLevel = configuration->deepAt("Logging:LogLevel:Default").get_string(); + const std::uint64_t secondArrayElement = configuration->deepAt("Array:1").get_unsigned(); std::cout << "MySetting: " << value << std::endl; std::cout << "Default LogLevel: " << defaultLogLevel << std::endl; @@ -169,9 +174,14 @@ The command line configuration source is added using the addCommandLine(argc, ar auto configuration = ConfigurationBuilder{}.addCommandLine(argc, argv).build(); ``` -**Argument pattern:** [--]setting[:nestedSetting|arrayIndex...][!type]=[value] +**Argument patterns:** -Setting the prefix '--' is optional. Nested settings are supported using the ':' separator. If the object is an array, +* setting[:nestedSetting|arrayIndex...][!type]=[value] +* --setting[:nestedSetting|arrayIndex...][!type] [value] +* /setting[:nestedSetting|arrayIndex...][!type] [value] + +Setting prefix '--' or '/' is optional when value is followed by '='. Nested settings are supported using +the ':' separator. If the object is an array, numbers can be used to address the proper element. By default, setting values are saved as strings, but other types are also supported using the '!' mark. If a value is not provided, the default one will be used for the specified type, see [supported types](#supported-types). @@ -208,13 +218,14 @@ type (default value) - Description #### Example Command Line Arguments -- --MySetting="hello" - will override or create a MySetting setting with the "hello" string value. -- --Switch!bool=true - will override or create a Switch setting with a true bool value. +- MySetting="hello" - will override or create a MySetting setting with the "hello" string value. +- Switch!bool=true - will override or create a Switch setting with a true bool value. +- --MySetting hello - will override or create a MySetting setting with the "hello" string value. - --Offset!double - will override or create an Offset setting with the default 0.0 double value. -- --Logging:LogLevel:Default=Warning - will override or create a nested setting with the string "Warning" value. -- --Strings:2=hello - will override or create a third element in the Strings array setting with the string "hello" +- --Logging:LogLevel:Default Warning - will override or create a nested setting with the string "Warning" value. +- /Strings:2 hello - will override or create a third element in the Strings array setting with the string "hello" value. -- --Array:1!uint=123 will override the second element in Array with the unsigned integer 123 value. +- /Array:1!uint 123 will override the second element in Array with the unsigned integer 123 value. ### Environment Variables @@ -373,15 +384,12 @@ using namespace sb::cf; class CustomConfigurationProvider : public IConfigurationProvider { - private: JsonObject _configuration; public: void load() override { _configuration = {{"mysettingOne", "value1"}, {"mysettingTwo", "value2"}}; } - JsonObject &getConfiguration() override { return _configuration; } - - const JsonObject &getConfiguration() const override { return _configuration; } + [[nodiscard]] const JsonObject &getConfiguration() const override { return _configuration; } }; class CustomConfigurationSource : public IConfigurationSource @@ -395,7 +403,7 @@ class CustomConfigurationSource : public IConfigurationSource int main(int argc, char **argv) { - IConfiguration::Ptr configuration = + const IConfiguration::Ptr configuration = ConfigurationBuilder{}.add(std::make_unique()).build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index aa92e55..00b51cc 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -19,7 +19,7 @@ if (_7BIT_CONF_BUILD_INTEGRATION_TESTS) endif () if (_7BIT_CONF_BUILD_E2E_TESTS) - add_subdirectory(EndToEnd) + add_subdirectory(E2E) endif () file(GLOB_RECURSE FILES CONFIGURE_DEPENDS ./Helpers/Files/*) diff --git a/Tests/EndToEnd/CMakeLists.txt b/Tests/E2E/CMakeLists.txt similarity index 88% rename from Tests/EndToEnd/CMakeLists.txt rename to Tests/E2E/CMakeLists.txt index 0a8ccc5..2935de4 100644 --- a/Tests/EndToEnd/CMakeLists.txt +++ b/Tests/E2E/CMakeLists.txt @@ -4,6 +4,6 @@ target_link_libraries(Echo 7bitConf) find_package(Python REQUIRED) -add_test(NAME EndToEnd.Echo +add_test(NAME E2E.Echo COMMAND ${Python_EXECUTABLE} echo_test.py $ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Tests/E2E/echo.cpp b/Tests/E2E/echo.cpp new file mode 100644 index 0000000..449e4eb --- /dev/null +++ b/Tests/E2E/echo.cpp @@ -0,0 +1,14 @@ +#include <../../Include/SevenBit/Conf.hpp> +#include + +using namespace sb::cf; + +int main(const int argc, char **argv) +{ + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addCommandLine(argc, argv) + .addEnvironmentVariables() + .build(); + std::cout << *configuration; + return 0; +} diff --git a/Tests/EndToEnd/echoTestData.json b/Tests/E2E/echoTestData.json similarity index 72% rename from Tests/EndToEnd/echoTestData.json rename to Tests/E2E/echoTestData.json index 30a2a33..9c5aa49 100644 --- a/Tests/EndToEnd/echoTestData.json +++ b/Tests/E2E/echoTestData.json @@ -1,42 +1,42 @@ [ { "args": [ - "testarg=123" + "arg=123" ], "env": {}, "expected": { - "testarg": "123" + "arg": "123" } }, { "args": [ - "--testarg", + "--arg", "123" ], "env": {}, "expected": { - "testarg": "123" + "arg": "123" } }, { "args": [ - "--testarg", + "--arg", "123", - "/testarg2", + "/arg2", "12", - "testarg3=321", - "testarg4=2", - "testarg5", - "--testarg6" + "arg3=321", + "arg4=2", + "arg5", + "--arg6" ], "env": {}, "expected": { - "testarg": "123", - "testarg2": "12", - "testarg3": "321", - "testarg4": "2", - "testarg5": "", - "testarg6": "" + "arg": "123", + "arg2": "12", + "arg3": "321", + "arg4": "2", + "arg5": "", + "arg6": "" } }, { @@ -45,7 +45,7 @@ "123", "/number:nested!int", "12", - "unumber!uint=321", + "unsignedNumber!uint=321", "flag!bool=true", "--json!json", "[1,2,3,4,5,6]", @@ -81,7 +81,7 @@ }, "number2": 321, "str": "123", - "unumber": 321 + "unsignedNumber": 321 } } ] diff --git a/Tests/E2E/echo_test.py b/Tests/E2E/echo_test.py new file mode 100644 index 0000000..3896bb0 --- /dev/null +++ b/Tests/E2E/echo_test.py @@ -0,0 +1,60 @@ +import json +import os +import subprocess +import sys + + +def get_echo_exec_path(): + if len(sys.argv) != 2: + raise Exception("Echo executable not provided") + echo_exec = sys.argv[1] + if not os.path.exists(echo_exec): + raise Exception("Echo executable does not exist") + return echo_exec + + +class EchoTest: + def __init__(self, echo_exec_path): + self.echoExecPath = echo_exec_path + self.testsData = self.__get_tests_data() + + @staticmethod + def __get_tests_data(): + with open('echoTestData.json') as data: + return json.load(data) + + def __run_test(self, args, env, expected_json): + result = subprocess.run([self.echoExecPath, *args], env=env, capture_output=True, text=True) + if result.returncode: + raise Exception(f"test returned non zero code {result.returncode}") + output = json.dumps(json.loads(result.stdout)) + expected = json.dumps(expected_json) + if expected != output: + raise Exception(f"result of running test: '{output}' does not match expected: '{expected}'") + + def __run_test_and_summarize(self, test_data): + args = test_data["args"] + env = test_data["env"] + expected_json = test_data["expected"] + try: + self.__run_test(args, env, expected_json) + print(f"Test for args: {args}, env: {env} succeeded") + return True + except Exception as e: + print(f"Test for args: {args}, env: {env} failed: {e}") + return False + + def run(self): + all_tests = len(self.testsData) + succeeded_tests = 0 + for count, testData in enumerate(self.testsData, start=1): + print(f"Test {count}/{all_tests}") + succeeded_tests += self.__run_test_and_summarize(testData) + if succeeded_tests == all_tests: + print(f"All test succeeded: {succeeded_tests}/{all_tests}") + else: + raise Exception(f"Some tests failed: {all_tests - succeeded_tests}/{all_tests}") + + +if __name__ == "__main__": + EchoTest(get_echo_exec_path()).run() diff --git a/Tests/EndToEnd/echo.cpp b/Tests/EndToEnd/echo.cpp deleted file mode 100644 index 68fba0f..0000000 --- a/Tests/EndToEnd/echo.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include <../../Include/SevenBit/Conf.hpp> -#include - -using namespace sb::cf; - -int main(int argc, char **argv) -{ - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addCommandLine(argc, argv) - .addEnvironmentVariables() - .build(); - std::cout << *configuration; - return 0; -} diff --git a/Tests/EndToEnd/echo_test.py b/Tests/EndToEnd/echo_test.py deleted file mode 100644 index 96fbc43..0000000 --- a/Tests/EndToEnd/echo_test.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import os -import subprocess -import sys - - -def getEchoExecPath(): - if len(sys.argv) != 2: - raise Exception("Echo executable not provided") - echoExec = sys.argv[1] - if not os.path.exists(echoExec): - raise Exception("Echo executable does not exist") - return echoExec - - -class EchoTest: - def __init__(self, echoExecPath): - self.echoExecPath = echoExecPath - self.testsData = self.__getTestsData() - - def __getTestsData(self): - with open('echoTestData.json') as data: - return json.load(data) - - def __runTest(self, args, env, expectedJson): - result = subprocess.run([self.echoExecPath, *args], env=env, capture_output=True, text=True) - if result.returncode: - raise Exception(f"test returned non zero code {result.returncode}") - formatedOutput = json.dumps(json.loads(result.stdout)) - formatedExpected = json.dumps(expectedJson) - if formatedExpected != formatedOutput: - raise Exception(f"result of running test: '{formatedOutput}' does not match expected: '{formatedExpected}'") - - def __runTestAndSummarize(self, testData): - args = testData["args"] - env = testData["env"] - expectedJson = testData["expected"] - try: - self.__runTest(args, env, expectedJson) - print(f"Test for args: {args}, env: {env} succeeded") - return True - except Exception as e: - print(f"Test for args: {args}, env: {env} failed: {e}") - return False - - def run(self): - allTests = len(self.testsData) - succeededTests = 0 - for count, testData in enumerate(self.testsData, start=1): - print(f"Test {count}/{allTests}") - succeededTests += self.__runTestAndSummarize(testData) - if succeededTests == allTests: - print(f"All test succeeded: {succeededTests}/{allTests}") - else: - raise Exception(f"Some tests failed: {allTests - succeededTests}/{allTests}") - - -if __name__ == "__main__": - EchoTest(getEchoExecPath()).run() diff --git a/Tests/Helpers/Classes/CustomConfigSource.hpp b/Tests/Helpers/Classes/CustomConfigSource.hpp index e50f561..77289ee 100644 --- a/Tests/Helpers/Classes/CustomConfigSource.hpp +++ b/Tests/Helpers/Classes/CustomConfigSource.hpp @@ -9,7 +9,7 @@ class CustomConfigSource : public sb::cf::IConfigurationSource { - sb::cf::IConfigurationProvider::Ptr build(sb::cf::IConfigurationBuilder &builder) + sb::cf::IConfigurationProvider::Ptr build(sb::cf::IConfigurationBuilder &builder) override { sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get()++; diff --git a/Tests/Helpers/Files/Directory/settingTwo.json b/Tests/Helpers/Files/Directory/settingTwo.json index cf81edc..06f63f3 100644 --- a/Tests/Helpers/Files/Directory/settingTwo.json +++ b/Tests/Helpers/Files/Directory/settingTwo.json @@ -1,8 +1,10 @@ { - "array": [1], - "string": "stringdev", + "array": [ + 1 + ], + "string": "dev", "object": { - "string": "stringdev", + "string": "dev", "inner": { "num": 12345 } diff --git a/Tests/Integration/ConfigurationTest.cpp b/Tests/Integration/ConfigurationTest.cpp index bffcc29..7d95eaf 100644 --- a/Tests/Integration/ConfigurationTest.cpp +++ b/Tests/Integration/ConfigurationTest.cpp @@ -19,42 +19,42 @@ class ConfigurationTest : public testing::Test TEST_F(ConfigurationTest, ShouldLoadConfig) { - auto conf = sb::cf::ConfigurationBuilder{} - .addAppSettings("dev") - .addJson({{"string", 1}}) - .addCommandLine({"--string", "2", "Array:0!int=33"}) - .AddInMemory("set:set", 44444) - .addKeyPerFile("Directory") - .build(); + const auto conf = sb::cf::ConfigurationBuilder{} + .addAppSettings("dev") + .addJson({{"string", 1}}) + .addCommandLine({"--string", "2", "Array:0!int=33"}) + .AddInMemory("set:set", 44444) + .addKeyPerFile("Directory") + .build(); - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{33, 2, 3, 4, 5}}, - {"MySetting", "appsettings.dev.json Value"}, - {"ExtraSetting", "extra appsettings.dev.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}, - {"set", {{"set", 44444}}}, - {"string", "2"}, - {"settingOne", - {{"number", 12345}, - {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, - {"string", "string"}, - {"object", {{"num", 134}, {"string", "string"}}}}}, - {"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + const sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{33, 2, 3, 4, 5}}, + {"MySetting", "appsettings.dev.json Value"}, + {"ExtraSetting", "extra appsettings.dev.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}, + {"set", {{"set", 44444}}}, + {"string", "2"}, + {"settingOne", + {{"number", 12345}, + {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, + {"string", "string"}, + {"object", {{"num", 134}, {"string", "string"}}}}}, + {"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; EXPECT_EQ(conf->root(), expected); } TEST_F(ConfigurationTest, ShouldFindConfgValues) { - auto conf = sb::cf::ConfigurationBuilder{} - .addAppSettings("dev") - .addJson({{"string", 1}}) - .addCommandLine({"--string", "2", "Array:0!int=33"}) - .AddInMemory("set:set", 44444) - .addKeyPerFile("Directory") - .build(); + const auto conf = sb::cf::ConfigurationBuilder{} + .addAppSettings("dev") + .addJson({{"string", 1}}) + .addCommandLine({"--string", "2", "Array:0!int=33"}) + .AddInMemory("set:set", 44444) + .addKeyPerFile("Directory") + .build(); EXPECT_TRUE(conf->find("Array")); EXPECT_TRUE(conf->find("MySetting")); @@ -62,19 +62,19 @@ TEST_F(ConfigurationTest, ShouldFindConfgValues) EXPECT_TRUE(conf->deepFind({"settingTwo", "array", "0"})); EXPECT_TRUE(conf->deepFind("settingTwo:array:0")); EXPECT_EQ(conf->at("Array"), (sb::cf::JsonArray{33, 2, 3, 4, 5})); - EXPECT_EQ(conf->deepAt("settingOne:number"), (std::int64_t{12345})); - EXPECT_EQ(conf->deepAt("settingTwo:array:0"), (std::int64_t{1})); - EXPECT_EQ(conf->operator[]("settingTwo:array:0"), (std::int64_t{1})); - EXPECT_EQ(conf->operator[]({"settingTwo", "array", "0"}), (std::int64_t{1})); + EXPECT_EQ(conf->deepAt("settingOne:number"), std::int64_t{12345}); + EXPECT_EQ(conf->deepAt("settingTwo:array:0"), std::int64_t{1}); + EXPECT_EQ(conf->operator[]("settingTwo:array:0"), std::int64_t{1}); + EXPECT_EQ(conf->operator[]({"settingTwo", "array", "0"}), std::int64_t{1}); } TEST_F(ConfigurationTest, ShouldSerializeValues) { - auto conf = sb::cf::ConfigurationBuilder{} - .addJson({{"string", 1}}) - .addCommandLine({"--string", "2", "Array:0!int=33"}) - .AddInMemory("set:set", 44444) - .build(); + const auto conf = sb::cf::ConfigurationBuilder{} + .addJson({{"string", 1}}) + .addCommandLine({"--string", "2", "Array:0!int=33"}) + .AddInMemory("set:set", 44444) + .build(); std::string expected = R"({"Array":[33,2,3,4,5],"MySetting":""})"; std::ostringstream stream; diff --git a/Tests/Unit/CommandLineParserBuilderTest.cpp b/Tests/Unit/CommandLineParserBuilderTest.cpp index aa4668c..7e68dee 100644 --- a/Tests/Unit/CommandLineParserBuilderTest.cpp +++ b/Tests/Unit/CommandLineParserBuilderTest.cpp @@ -25,9 +25,9 @@ class CommandLineParserBuilderTest : public testing::Test TEST_F(CommandLineParserBuilderTest, ShouldBuildDefault) { - auto parser = sb::cf::CommandLineParserBuilder{}.build(); + const auto parser = sb::cf::CommandLineParserBuilder{}.build(); - auto &casted = dynamic_cast(*parser); + const auto &casted = dynamic_cast(*parser); auto &splitter = dynamic_cast(casted.getOptionsSplitter()); auto &deserializers = @@ -35,9 +35,9 @@ TEST_F(CommandLineParserBuilderTest, ShouldBuildDefault) EXPECT_EQ(casted.getOptionPrefixes(), (std::vector{"--", "/"})); EXPECT_TRUE(casted.getConsiderSeparated()); - EXPECT_EQ(splitter.getKeySplitters(), (std::vector{":"})); - EXPECT_EQ(splitter.getSettingSplitters(), (std::vector{"="})); - EXPECT_EQ(splitter.getTypeMarkers(), (std::vector{"!"})); + EXPECT_EQ(splitter.getKeySplitters(), std::vector{":"}); + EXPECT_EQ(splitter.getSettingSplitters(), std::vector{"="}); + EXPECT_EQ(splitter.getTypeMarkers(), std::vector{"!"}); EXPECT_FALSE(splitter.getAllowEmptyKeys()); std::vector types; @@ -112,9 +112,9 @@ TEST_F(CommandLineParserBuilderTest, ShouldUseCustomValueDeserializerMap) { sb::cf::CommandLineParserBuilder builder; - auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); + const auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); - auto &casted = dynamic_cast(*parser); + const auto &casted = dynamic_cast(*parser); EXPECT_TRUE(dynamic_cast(&casted.getOptionsSplitter())); EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); @@ -124,9 +124,9 @@ TEST_F(CommandLineParserBuilderTest, ShouldUseCustomSplitter) { sb::cf::CommandLineParserBuilder builder; - auto parser = builder.useSplitter(std::make_unique()).build(); + const auto parser = builder.useSplitter(std::make_unique()).build(); - auto &casted = dynamic_cast(*parser); + const auto &casted = dynamic_cast(*parser); EXPECT_TRUE(dynamic_cast(&casted.getOptionsSplitter())); EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); diff --git a/Tests/Unit/ConfigurationBuilderTest.cpp b/Tests/Unit/ConfigurationBuilderTest.cpp index 838b1f0..8c7830c 100644 --- a/Tests/Unit/ConfigurationBuilderTest.cpp +++ b/Tests/Unit/ConfigurationBuilderTest.cpp @@ -85,7 +85,7 @@ TEST_F(ConfigurationBuilderTest, ShouldBuildConfigWithProperties) .add(std::make_unique()) .build(); - auto cnt = sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get(); + const auto cnt = sb::cf::ObjectHolder::safeCastFrom(*builder.getProperties()["counter"]).get(); EXPECT_EQ(cnt, 4); } diff --git a/Tests/Unit/Details/ContainerUtilsTest.cpp b/Tests/Unit/Details/ContainerUtilsTest.cpp index 40b14ea..7c2eb6a 100644 --- a/Tests/Unit/Details/ContainerUtilsTest.cpp +++ b/Tests/Unit/Details/ContainerUtilsTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "SevenBit/Conf/Details/ContainerUtils.hpp" @@ -14,7 +13,7 @@ class ContainerUtilsTest : public testing::Test void TearDown() override {} - ~ContainerUtilsTest() {} + ~ContainerUtilsTest() override = default; static void TearDownTestSuite() {} }; @@ -23,7 +22,7 @@ TEST_F(ContainerUtilsTest, ShouldEraseElements) { std::vector vec = {1, 2, 3, 4, 5, 6}; - auto removed = sb::cf::details::ContainerUtils::eraseIf(vec, [](int val) { return val % 2; }); + const auto removed = sb::cf::details::ContainerUtils::eraseIf(vec, [](const int val) { return val % 2; }); EXPECT_EQ(removed, 3); EXPECT_EQ(vec, (std::vector{2, 4, 6})); diff --git a/Tests/Unit/Details/JsonExtTest.cpp b/Tests/Unit/Details/JsonExtTest.cpp index 01b8c82..343d9bb 100644 --- a/Tests/Unit/Details/JsonExtTest.cpp +++ b/Tests/Unit/Details/JsonExtTest.cpp @@ -34,7 +34,7 @@ PARAMS_TEST(JsonExtTest, ShouldFing, FindData) {"str", "hello"}, {"number", 123}, {"array", sb::cf::JsonArray{{{"key", "value"}}}}, {"inner", -123}}; auto &[keys, expectedFound, expectedValue] = GetParam(); - auto valuePtr = sb::cf::details::JsonExt::find(json, keys); + const auto valuePtr = sb::cf::details::JsonExt::find(json, keys); EXPECT_EQ(!!valuePtr, expectedFound); if (valuePtr) { @@ -74,7 +74,7 @@ PARAMS_TEST(JsonExtTest, ShouldDeepFing, DeepFindData) }}}}}; auto &[keys, expectedFound, expectedValue] = GetParam(); - auto valuePtr = sb::cf::details::JsonExt::deepFind(json, keys); + const auto valuePtr = sb::cf::details::JsonExt::deepFind(json, keys); EXPECT_EQ(!!valuePtr, expectedFound); if (valuePtr) { @@ -98,18 +98,18 @@ TEST_F(JsonExtTest, ShouldDeepGetOrOverride) sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:str") = "hello3"; sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:inner:str") = "hello5"; - sb::cf::JsonObject expectedJson = {{"str", "hello"}, - {"number", 123}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - {{"str", "hello3"}, - {"number", 1232}, - {"inner", - { - {"str", "hello5"}, - }}}}}}}; + const sb::cf::JsonObject expectedJson = {{"str", "hello"}, + {"number", 123}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + {{"str", "hello3"}, + {"number", 1232}, + {"inner", + { + {"str", "hello5"}, + }}}}}}}; EXPECT_EQ(json, expectedJson); } @@ -120,7 +120,7 @@ TEST_F(JsonExtTest, ShouldDeepGetOrOverrideArrayElement) sb::cf::details::JsonExt::deepGetOrOverride(json, "array:3:object") = "value"; - sb::cf::JsonObject expected = { + const sb::cf::JsonObject expected = { {"str", "hello"}, {"array", sb::cf::JsonArray{sb::cf::JsonValue{}, sb::cf::JsonValue{}, sb::cf::JsonValue{}, {{"object", "value"}}}}, @@ -135,7 +135,7 @@ TEST_F(JsonExtTest, ShouldDeepGetOrOverrideExistingArrayElement) sb::cf::details::JsonExt::deepGetOrOverride(json, "array:3:object") = "value"; - sb::cf::JsonObject expected = { + const sb::cf::JsonObject expected = { {"str", "hello"}, {"array", sb::cf::JsonArray{1234, {{"second", "element"}}, sb::cf::JsonValue{}, {{"object", "value"}}}}, }; @@ -149,7 +149,7 @@ TEST_F(JsonExtTest, ShouldDeepGetOrOverrideWrongArrayElement) sb::cf::details::JsonExt::deepGetOrOverride(json, "array:-3:object") = "value"; - sb::cf::JsonObject expected = {{"str", "hello"}, {"array", {{"-3", {{"object", "value"}}}}}}; + const sb::cf::JsonObject expected = {{"str", "hello"}, {"array", {{"-3", {{"object", "value"}}}}}}; EXPECT_EQ(json, expected); } @@ -169,16 +169,16 @@ TEST_F(JsonExtTest, ShouldDeepGetOrOverrideDestroy) sb::cf::details::JsonExt::deepGetOrOverride(json, "inner:inner:str:fail") = "value"; - sb::cf::JsonObject expected = {{"str", "hello"}, - {"number", 123}, - {"inner", - {{"str", "hello1"}, - {"number", 1231}, - {"inner", - { - {"str", {{"fail", "value"}}}, - {"number", 1232}, - }}}}}; + const sb::cf::JsonObject expected = {{"str", "hello"}, + {"number", 123}, + {"inner", + {{"str", "hello1"}, + {"number", 1231}, + {"inner", + { + {"str", {{"fail", "value"}}}, + {"number", 1232}, + }}}}}; EXPECT_EQ(json, expected); } @@ -230,18 +230,18 @@ TEST_F(JsonExtTest, SouldDeepMergeJsonValue) sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); - sb::cf::JsonObject expectedJson = {{"str", "helloOv"}, - {"number", 123}, - {"array", sb::cf::JsonArray{3, {{"value", "value"}}, 1, 4, 5, 6}}, - {"default", "default"}, - {"inner", - {{"str", "hello1"}, - {"number", 12313}, - {"inner", - { - {"str", "hello2Ov"}, - {"number", 12323}, - }}}}}; + const sb::cf::JsonObject expectedJson = {{"str", "helloOv"}, + {"number", 123}, + {"array", sb::cf::JsonArray{3, {{"value", "value"}}, 1, 4, 5, 6}}, + {"default", "default"}, + {"inner", + {{"str", "hello1"}, + {"number", 12313}, + {"inner", + { + {"str", "hello2Ov"}, + {"number", 12323}, + }}}}}; EXPECT_EQ(json, expectedJson); } @@ -267,15 +267,15 @@ TEST_F(JsonExtTest, SouldDeepMergeJsonArray) sb::cf::details::JsonExt::deepMerge(json, std::move(jsonOverride)); - sb::cf::JsonArray expectedJson{{{"str", "hello22"}}, - {{"number", 123}}, - {{"number", 123}}, - {{"inner", - { - {"str", "hello1"}, - {"number", 1231}, + const sb::cf::JsonArray expectedJson{{{"str", "hello22"}}, + {{"number", 123}}, + {{"number", 123}}, + {{"inner", + { + {"str", "hello1"}, + {"number", 1231}, - }}}}; + }}}}; EXPECT_EQ(json, expectedJson); } diff --git a/Tests/Unit/Details/RequireTest.cpp b/Tests/Unit/Details/RequireTest.cpp index ac7177c..d4859e5 100644 --- a/Tests/Unit/Details/RequireTest.cpp +++ b/Tests/Unit/Details/RequireTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "SevenBit/Conf/Details/Require.hpp" @@ -14,7 +13,7 @@ class RequireTest : public testing::Test void TearDown() override {} - ~RequireTest() {} + ~RequireTest() override = default; static void TearDownTestSuite() {} }; diff --git a/Tests/Unit/Details/SettingSplitterTest.cpp b/Tests/Unit/Details/SettingSplitterTest.cpp index 93f59e1..771894f 100644 --- a/Tests/Unit/Details/SettingSplitterTest.cpp +++ b/Tests/Unit/Details/SettingSplitterTest.cpp @@ -49,7 +49,7 @@ static Params SplitS PARAMS_TEST(SettingSplitterTest, ShouldSplitSetting, SplitSettingData) { const auto &[setting, expected] = GetParam(); - sb::cf::details::SettingSplitter splitter{{"=", ";"}, {"!", "___"}, {":", "__"}, true}; + const sb::cf::details::SettingSplitter splitter{{"=", ";"}, {"!", "___"}, {":", "__"}, true}; EXPECT_EQ(splitter.split(setting), expected); } @@ -74,10 +74,10 @@ PARAMS_TEST_COMBINED_4(SettingSplitterTest, ShouldSplitWithDifferentSplitters, S const auto &[settingSplitter, typeMarker, keySplitter, setting] = GetParam(); const auto &[keys, type, value] = setting; - sb::cf::details::SettingSplitter splitter{{settingSplitter}, {typeMarker}, {keySplitter}, true}; + const sb::cf::details::SettingSplitter splitter{{settingSplitter}, {typeMarker}, {keySplitter}, true}; - auto key = sb::cf::details::StringUtils::join(keys, keySplitter); - auto fullSetting = key + typeMarker + type + settingSplitter + value; + const auto key = sb::cf::details::StringUtils::join(keys, keySplitter); + const auto fullSetting = key + typeMarker + type + settingSplitter + value; EXPECT_EQ(splitter.split(fullSetting), (sb::cf::ISettingSplitter::Result{keys, type, value})); } @@ -118,7 +118,7 @@ PARAMS_TEST(SettingSplitterTest, ShouldSplitWithEmptySplitters, EmptySplittersDa { const auto &[settingSplitters, typeMarkers, keySplitters, expected] = GetParam(); - sb::cf::details::SettingSplitter splitter{settingSplitters, typeMarkers, keySplitters, true}; + const sb::cf::details::SettingSplitter splitter{settingSplitters, typeMarkers, keySplitters, true}; EXPECT_EQ(splitter.split("key:deep!int=value"), expected); } diff --git a/Tests/Unit/Details/StringUtilsTest.cpp b/Tests/Unit/Details/StringUtilsTest.cpp index f47fe7f..5e6343e 100644 --- a/Tests/Unit/Details/StringUtilsTest.cpp +++ b/Tests/Unit/Details/StringUtilsTest.cpp @@ -274,7 +274,6 @@ Params, std::optional, sb::cf::JsonVal PARAMS_TEST(ValueDeserializersMapTest, ShouldDeserializeValue, DeserializeData) { const auto &[type, value, expected] = GetParam(); - auto deserializers = makeDefaultDeserializersMap("string", false); + const auto deserializers = makeDefaultDeserializersMap("string", false); auto &deserializer = deserializers.getDeserializerFor(type); EXPECT_EQ(deserializer.deserialize(value), expected); @@ -161,10 +161,10 @@ PARAMS_TEST(ValueDeserializersMapTest, ShouldDeserializeValue, DeserializeData) TEST_F(ValueDeserializersMapTest, ShouldDeserializeEmptyJsonOption) { - auto deserializers = makeDefaultDeserializersMap(); + const auto deserializers = makeDefaultDeserializersMap(); auto &deserializer = deserializers.getDeserializerFor("json"); - EXPECT_EQ((deserializer.deserialize(std::nullopt)), (sb::cf::JsonValue{})); + EXPECT_EQ(deserializer.deserialize(std::nullopt), sb::cf::JsonValue{}); } TEST_F(ValueDeserializersMapTest, ShouldNotFoundDeserializer) @@ -234,7 +234,7 @@ static Params> FailDeserialize PARAMS_TEST(ValueDeserializersMapTest, ShouldFailDeserialize, FailDeserializeValues) { const auto &[type, value] = GetParam(); - auto deserializers = makeDefaultDeserializersMap(); + const auto deserializers = makeDefaultDeserializersMap(); auto &deserializer = deserializers.getDeserializerFor(type); diff --git a/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp b/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp index 8d7d585..731b180 100644 --- a/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp +++ b/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp @@ -25,16 +25,16 @@ class EnvironmentVarsParserBuilderTest : public testing::Test TEST_F(EnvironmentVarsParserBuilderTest, ShouldBuildDefault) { - auto parser = sb::cf::EnvironmentVarsParserBuilder{}.build(); + const auto parser = sb::cf::EnvironmentVarsParserBuilder{}.build(); - auto &casted = dynamic_cast(*parser); + const auto &casted = dynamic_cast(*parser); auto &splitter = dynamic_cast(casted.getSettingSplitter()); auto &deserializers = dynamic_cast(casted.getValueDeserializersMap()); EXPECT_EQ(splitter.getKeySplitters(), (std::vector{":", "__"})); - EXPECT_EQ(splitter.getSettingSplitters(), (std::vector{"="})); + EXPECT_EQ(splitter.getSettingSplitters(), std::vector{"="}); EXPECT_EQ(splitter.getTypeMarkers(), (std::vector{"!", "___"})); EXPECT_FALSE(splitter.getAllowEmptyKeys()); @@ -107,9 +107,9 @@ TEST_F(EnvironmentVarsParserBuilderTest, ShouldUseCustomValueDeserializerMap) { sb::cf::EnvironmentVarsParserBuilder builder; - auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); + const auto parser = builder.useValueDeserializersMap(std::make_unique()).build(); - auto &casted = dynamic_cast(*parser); + const auto &casted = dynamic_cast(*parser); EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); @@ -119,9 +119,9 @@ TEST_F(EnvironmentVarsParserBuilderTest, ShouldUseCustomSplitter) { sb::cf::EnvironmentVarsParserBuilder builder; - auto parser = builder.useSplitter(std::make_unique()).build(); + const auto parser = builder.useSplitter(std::make_unique()).build(); - auto &casted = dynamic_cast(*parser); + const auto &casted = dynamic_cast(*parser); EXPECT_TRUE(dynamic_cast(&casted.getSettingSplitter())); EXPECT_TRUE(dynamic_cast(&casted.getValueDeserializersMap())); diff --git a/Tests/Unit/ObjectHolderTest.cpp b/Tests/Unit/ObjectHolderTest.cpp index b81c6fc..9ea8dca 100644 --- a/Tests/Unit/ObjectHolderTest.cpp +++ b/Tests/Unit/ObjectHolderTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "SevenBit/Conf/ObjectHolder.hpp" @@ -24,9 +23,10 @@ TEST_F(ObjectHolderTest, ShouldCastHolder) const sb::cf::IObject::Ptr holder = sb::cf::ObjectHolder::from(1); auto &casted = sb::cf::ObjectHolder::castFrom(*holder); - auto &casted2 = sb::cf::ObjectHolder::safeCastFrom(*holder); + auto &safeCasted = sb::cf::ObjectHolder::safeCastFrom(*holder); EXPECT_EQ(casted.get(), 1); + EXPECT_EQ(&casted, &safeCasted); auto act = [&] { auto &_ = sb::cf::ObjectHolder::safeCastFrom(*holder); }; EXPECT_THROW(act(), std::bad_cast); diff --git a/Tests/Unit/Sources/AppSettingsConfigurationTest.cpp b/Tests/Unit/Sources/AppSettingsConfigurationTest.cpp index 6e55734..707b22d 100644 --- a/Tests/Unit/Sources/AppSettingsConfigurationTest.cpp +++ b/Tests/Unit/Sources/AppSettingsConfigurationTest.cpp @@ -22,27 +22,27 @@ class AppSettingsConfiguration : public testing::Test TEST_F(AppSettingsConfiguration, ShouldLoadAppSettings) { - auto provider = sb::cf::AppSettingsConfigurationSource::create()->build(mock); + const auto provider = sb::cf::AppSettingsConfigurationSource::create()->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, - {"MySetting", "appsettings.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; + const sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, + {"MySetting", "appsettings.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(AppSettingsConfiguration, ShouldLoadDevAppSettings) { - auto provider = sb::cf::AppSettingsConfigurationSource::create("dev")->build(mock); + const auto provider = sb::cf::AppSettingsConfigurationSource::create("dev")->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, - {"MySetting", "appsettings.dev.json Value"}, - {"ExtraSetting", "extra appsettings.dev.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; + const sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, + {"MySetting", "appsettings.dev.json Value"}, + {"ExtraSetting", "extra appsettings.dev.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } diff --git a/Tests/Unit/Sources/ChainedConfigurationTest.cpp b/Tests/Unit/Sources/ChainedConfigurationTest.cpp index c93f262..d4ab505 100644 --- a/Tests/Unit/Sources/ChainedConfigurationTest.cpp +++ b/Tests/Unit/Sources/ChainedConfigurationTest.cpp @@ -25,7 +25,7 @@ class ChainedConfigurationTest : public testing::Test TEST_F(ChainedConfigurationTest, ShouldLoadEmptyChainConfig) { - auto provider = sb::cf::ChainedConfigurationSource::create()->build(mock); + const auto provider = sb::cf::ChainedConfigurationSource::create()->build(mock); provider->load(); @@ -39,7 +39,7 @@ TEST_F(ChainedConfigurationTest, ShouldFailCreationDueToNullSource) TEST_F(ChainedConfigurationTest, ShouldFailAddDueToNullSource) { - auto source = + const auto source = sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json")}); EXPECT_THROW(source->add(nullptr), sb::cf::NullPointerException); @@ -47,22 +47,22 @@ TEST_F(ChainedConfigurationTest, ShouldFailAddDueToNullSource) TEST_F(ChainedConfigurationTest, ShouldLoadSimpleChainedConfig) { - auto provider = + const auto provider = sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json")}) ->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, - {"MySetting", "appsettings.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; + const sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, + {"MySetting", "appsettings.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(ChainedConfigurationTest, ShouldLoadComplexChainedConfig) { - auto provider = + const auto provider = sb::cf::ChainedConfigurationSource::create({sb::cf::JsonFileConfigurationSource::create("appsettings.json"), sb::cf::JsonFileConfigurationSource::create("appsettings.dev.json"), sb::cf::JsonConfigurationSource::create({{"number", 1}})}) @@ -70,11 +70,11 @@ TEST_F(ChainedConfigurationTest, ShouldLoadComplexChainedConfig) provider->load(); - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, - {"number", 1}, - {"MySetting", "appsettings.dev.json Value"}, - {"ExtraSetting", "extra appsettings.dev.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; + const sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{11, 2, 3, 4, 5}}, + {"number", 1}, + {"MySetting", "appsettings.dev.json Value"}, + {"ExtraSetting", "extra appsettings.dev.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Warning"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } diff --git a/Tests/Unit/Sources/CommandLineConfigurationTest.cpp b/Tests/Unit/Sources/CommandLineConfigurationTest.cpp index 4bba1b4..050dab9 100644 --- a/Tests/Unit/Sources/CommandLineConfigurationTest.cpp +++ b/Tests/Unit/Sources/CommandLineConfigurationTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "Mocks/ConfigurationBuilderMock.hpp" #include "SevenBit/Conf/Exceptions.hpp" @@ -23,55 +22,15 @@ class CommandLineConfigurationTest : public testing::Test TEST_F(CommandLineConfigurationTest, ShouldFailCreationDueToNullSource) { - std::vector args; + const std::vector args; EXPECT_THROW(auto result = sb::cf::CommandLineConfigurationSource::create(args, nullptr), sb::cf::NullPointerException); } TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgs) { - const char *const argv[] = {"program/path", - "--string!string=test", - "--string_sep!string", - "sep_value", - "--list:0!string=string", - "double!double=1.22", - "true_bool!bool=-1", - "false_bool!bool=0", - "false_bool2!bool=false", - "true_bool2!bool=true", - "--list:1=string1", - "list:2!string=string2", - "--object:inner:object=string", - "--int_list:0!int=33", - "--int_list:1!int=22", - "int_list:2!int=11", - "--last!int"}; - int size = sizeof(argv) / sizeof(char *); - auto provider = - sb::cf::CommandLineConfigurationSource::create(size, argv, sb::cf::CommandLineParserBuilder{}.build()) - ->build(mock); - - provider->load(); - - sb::cf::JsonObject expected = {{"string", "test"}, - {"string_sep", "sep_value"}, - {"double", 1.22}, - {"false_bool", false}, - {"false_bool2", false}, - {"true_bool", true}, - {"true_bool2", true}, - {"last", 0}, - {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"int_list", sb::cf::JsonArray{33, 22, 11}}, - {"object", {{"inner", {{"object", "string"}}}}}}; - - EXPECT_EQ(provider->getConfiguration(), expected); -} - -TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) -{ - std::vector args = {"--string!string=test", + constexpr const char *const argv[] = {"program/path", + "--string!string=test", "--string_sep!string", "sep_value", "--list:0!string=string", @@ -87,33 +46,73 @@ TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) "--int_list:1!int=22", "int_list:2!int=11", "--last!int"}; - auto provider = + constexpr int size = sizeof(argv) / sizeof(char *); + const auto provider = + sb::cf::CommandLineConfigurationSource::create(size, argv, sb::cf::CommandLineParserBuilder{}.build()) + ->build(mock); + + provider->load(); + + const sb::cf::JsonObject expected = {{"string", "test"}, + {"string_sep", "sep_value"}, + {"double", 1.22}, + {"false_bool", false}, + {"false_bool2", false}, + {"true_bool", true}, + {"true_bool2", true}, + {"last", 0}, + {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"int_list", sb::cf::JsonArray{33, 22, 11}}, + {"object", {{"inner", {{"object", "string"}}}}}}; + + EXPECT_EQ(provider->getConfiguration(), expected); +} + +TEST_F(CommandLineConfigurationTest, ShouldLoadConfFromArgsVector) +{ + const std::vector args = {"--string!string=test", + "--string_sep!string", + "sep_value", + "--list:0!string=string", + "double!double=1.22", + "true_bool!bool=-1", + "false_bool!bool=0", + "false_bool2!bool=false", + "true_bool2!bool=true", + "--list:1=string1", + "list:2!string=string2", + "--object:inner:object=string", + "--int_list:0!int=33", + "--int_list:1!int=22", + "int_list:2!int=11", + "--last!int"}; + const auto provider = sb::cf::CommandLineConfigurationSource::create(args, sb::cf::CommandLineParserBuilder{}.build())->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"string", "test"}, - {"double", 1.22}, - {"string_sep", "sep_value"}, - {"false_bool", false}, - {"false_bool2", false}, - {"true_bool", true}, - {"true_bool2", true}, - {"last", 0}, - {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"int_list", sb::cf::JsonArray{33, 22, 11}}, - {"object", {{"inner", {{"object", "string"}}}}}}; + const sb::cf::JsonObject expected = {{"string", "test"}, + {"double", 1.22}, + {"string_sep", "sep_value"}, + {"false_bool", false}, + {"false_bool2", false}, + {"true_bool", true}, + {"true_bool2", true}, + {"last", 0}, + {"list", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"int_list", sb::cf::JsonArray{33, 22, 11}}, + {"object", {{"inner", {{"object", "string"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(CommandLineConfigurationTest, ShouldLoadEmptyConfFromArgs) { - const char *argv[] = { + constexpr const char *argv[] = { "exec/path", }; - int size = sizeof(argv) / sizeof(char *); - auto provider = + constexpr int size = sizeof(argv) / sizeof(char *); + const auto provider = sb::cf::CommandLineConfigurationSource::create(size, argv, sb::cf::CommandLineParserBuilder{}.build()) ->build(mock); diff --git a/Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp b/Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp index 106a192..207e623 100644 --- a/Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp +++ b/Tests/Unit/Sources/EnvironmentVarsConfigurationTest.cpp @@ -29,7 +29,7 @@ class EnvironmentVarsConfigurationTest : public testing::Test _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__0___int=1"); _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__1___int=3"); _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_NUMBER_LIST__2___int=22"); - _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_JSON___json={\"key\": \"value\"}"); + _7BIT_CONF_PUT_ENV((char *)R"(7BIT_CONFIG_TEST_JSON___json={"key": "value"})"); _7BIT_CONF_PUT_ENV((char *)"7BIT_CONFIG_TEST_OBJECT__INNER__OBJECT=string"); _7BIT_CONF_PUT_ENV((char *)"7BIT_OTHER_CONFIG_TEST_STRING=string2"); } @@ -54,48 +54,48 @@ TEST_F(EnvironmentVarsConfigurationTest, ShouldFailProviderCreationDueToNullSour TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVars) { - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_CONFIG_", - sb::cf::EnvironmentVarsParserBuilder{}.build()) - ->build(mock); + const auto provider = sb::cf::EnvironmentVarsConfigurationSource::create( + "7BIT_CONFIG_", sb::cf::EnvironmentVarsParserBuilder{}.build()) + ->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"TEST_STRING", "test"}, - {"TEST_DOUBLE", 1.4}, - {"TEST_BOOL", true}, - {"TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, - {"TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, - {"TEST_JSON", {{"key", "value"}}}}; + const sb::cf::JsonObject expected = {{"TEST_STRING", "test"}, + {"TEST_DOUBLE", 1.4}, + {"TEST_BOOL", true}, + {"TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, + {"TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, + {"TEST_JSON", {{"key", "value"}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(EnvironmentVarsConfigurationTest, ShouldLoadConfFromEnvVarsWithPrefix) { - auto provider = + const auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BIT_", sb::cf::EnvironmentVarsParserBuilder{}.build()) ->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"CONFIG_TEST_STRING", "test"}, - {"CONFIG_TEST_DOUBLE", 1.4}, - {"CONFIG_TEST_BOOL", true}, - {"OTHER_CONFIG_TEST_STRING", "string2"}, - {"CONFIG_TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, - {"CONFIG_TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, - {"CONFIG_TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, - {"CONFIG_TEST_JSON", {{"key", "value"}}}}; + const sb::cf::JsonObject expected = {{"CONFIG_TEST_STRING", "test"}, + {"CONFIG_TEST_DOUBLE", 1.4}, + {"CONFIG_TEST_BOOL", true}, + {"OTHER_CONFIG_TEST_STRING", "string2"}, + {"CONFIG_TEST_NUMBER_LIST", sb::cf::JsonArray{1, 3, 22}}, + {"CONFIG_TEST_STRING_LIST", sb::cf::JsonArray{"string", "string1", "string2"}}, + {"CONFIG_TEST_OBJECT", {{"INNER", {{"OBJECT", "string"}}}}}, + {"CONFIG_TEST_JSON", {{"key", "value"}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(EnvironmentVarsConfigurationTest, ShouldNotLoadConfFromEnvVars) { - auto provider = sb::cf::EnvironmentVarsConfigurationSource::create("7BITCONFIGURATION_", - sb::cf::EnvironmentVarsParserBuilder{}.build()) - ->build(mock); + const auto provider = sb::cf::EnvironmentVarsConfigurationSource::create( + "7BITCONFIGURATION_", sb::cf::EnvironmentVarsParserBuilder{}.build()) + ->build(mock); provider->load(); diff --git a/Tests/Unit/Sources/InMemoryConfigurationTest.cpp b/Tests/Unit/Sources/InMemoryConfigurationTest.cpp index 2d4f9d2..73e16ed 100644 --- a/Tests/Unit/Sources/InMemoryConfigurationTest.cpp +++ b/Tests/Unit/Sources/InMemoryConfigurationTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "Mocks/ConfigurationBuilderMock.hpp" #include "SevenBit/Conf/Exceptions.hpp" @@ -28,12 +27,12 @@ TEST_F(InMemoryConfigurationTest, ShouldFailProviderCreationDueToNullSource) TEST_F(InMemoryConfigurationTest, ShouldLoadSimpleSettingConfiguration) { - auto provider = + const auto provider = sb::cf::InMemoryConfigurationSource::create({{"yes:yes:inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}})->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"yes", {{"yes", {{"inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}}}}}}; + const sb::cf::JsonObject expected = {{"yes", {{"yes", {{"inner", sb::cf::JsonArray{1, 2, 3, 4, 5}}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } diff --git a/Tests/Unit/Sources/JsonConfigurationTest.cpp b/Tests/Unit/Sources/JsonConfigurationTest.cpp index b95bf8c..2b3f382 100644 --- a/Tests/Unit/Sources/JsonConfigurationTest.cpp +++ b/Tests/Unit/Sources/JsonConfigurationTest.cpp @@ -27,11 +27,11 @@ TEST_F(JsonConfigurationTest, ShouldFailProviderCreationDueToNullSource) TEST_F(JsonConfigurationTest, ShouldLoadSimpleJsonConfig) { - auto provider = sb::cf::JsonConfigurationSource::create({{"hello", 12345}})->build(mock); + const auto provider = sb::cf::JsonConfigurationSource::create({{"hello", 12345}})->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"hello", 12345}}; + const sb::cf::JsonObject expected = {{"hello", 12345}}; EXPECT_EQ(provider->getConfiguration(), expected); } diff --git a/Tests/Unit/Sources/JsonFileConfigurationTest.cpp b/Tests/Unit/Sources/JsonFileConfigurationTest.cpp index f02cca2..288f326 100644 --- a/Tests/Unit/Sources/JsonFileConfigurationTest.cpp +++ b/Tests/Unit/Sources/JsonFileConfigurationTest.cpp @@ -27,20 +27,20 @@ TEST_F(JsonFileConfigurationTest, ShouldFailProviderCreationDueToNullSource) TEST_F(JsonFileConfigurationTest, ShouldLoadSimpleJsonConfigFile) { - auto provider = sb::cf::JsonFileConfigurationSource::create("appsettings.json")->build(mock); + const auto provider = sb::cf::JsonFileConfigurationSource::create("appsettings.json")->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, - {"MySetting", "appsettings.json Value"}, - {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; + const sb::cf::JsonObject expected = {{"Array", sb::cf::JsonArray{1, 2, 3, 4, 5}}, + {"MySetting", "appsettings.json Value"}, + {"Logging", {{"LogLevel", {{"Default", "Information"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(JsonFileConfigurationTest, ShouldNotLoadNonExistingJsonConfigFile) { - auto provider = sb::cf::JsonFileConfigurationSource::create("nonExisting.json", true)->build(mock); + const auto provider = sb::cf::JsonFileConfigurationSource::create("nonExisting.json", true)->build(mock); provider->load(); @@ -49,14 +49,14 @@ TEST_F(JsonFileConfigurationTest, ShouldNotLoadNonExistingJsonConfigFile) TEST_F(JsonFileConfigurationTest, ShouldFailLoadingNonExistingJsonConfigFile) { - auto provider = sb::cf::JsonFileConfigurationSource ::create("nonExisting.json")->build(mock); + const auto provider = sb::cf::JsonFileConfigurationSource ::create("nonExisting.json")->build(mock); EXPECT_THROW(provider->load(), sb::cf::ConfigFileNotFoundException); } TEST_F(JsonFileConfigurationTest, ShouldFailLoadingBadJsonConfigFile) { - auto provider = sb::cf::JsonFileConfigurationSource ::create("bad.json")->build(mock); + const auto provider = sb::cf::JsonFileConfigurationSource ::create("bad.json")->build(mock); EXPECT_THROW(provider->load(), sb::cf::BadConfigFileException); } diff --git a/Tests/Unit/Sources/JsonStreamConfigurationTest.cpp b/Tests/Unit/Sources/JsonStreamConfigurationTest.cpp index 6234237..d2d5ff4 100644 --- a/Tests/Unit/Sources/JsonStreamConfigurationTest.cpp +++ b/Tests/Unit/Sources/JsonStreamConfigurationTest.cpp @@ -30,13 +30,13 @@ TEST_F(JsonStreamConfigurationTest, ShouldLoadConfigFromStream) { std::stringstream stream; - stream << "{\"hello\": 12345, \"string\": \"asdf\"}"; + stream << R"({"hello": 12345, "string": "asdf"})"; - auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); + const auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"hello", 12345}, {"string", "asdf"}}; + const sb::cf::JsonObject expected = {{"hello", 12345}, {"string", "asdf"}}; EXPECT_EQ(provider->getConfiguration(), expected); } @@ -47,7 +47,7 @@ TEST_F(JsonStreamConfigurationTest, ShouldFailLoadingConfigFromBadStream) stream << "\"hello\""; - auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); + const auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); EXPECT_THROW(provider->load(), sb::cf::BadStreamException); } @@ -56,9 +56,9 @@ TEST_F(JsonStreamConfigurationTest, ShouldFailLoadingDueToDoubleStreamRead) { std::stringstream stream; - stream << "{\"hello\": 12345, \"string\": \"asdf\"}"; + stream << R"({"hello": 12345, "string": "asdf"})"; - auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); + const auto provider = sb::cf::JsonStreamConfigurationSource::create(stream)->build(mock); provider->load(); diff --git a/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp b/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp index 6fa0a15..9ca9754 100644 --- a/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp +++ b/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp @@ -22,14 +22,14 @@ class KeyPerFileConfigurationTest : public testing::Test TEST_F(KeyPerFileConfigurationTest, ShouldFailLoadingNonExistingDirectory) { - auto source = sb::cf::KeyPerFileConfigurationSource::create("nonexisting"); + const auto source = sb::cf::KeyPerFileConfigurationSource::create("nonexisting"); EXPECT_ANY_THROW(source->build(mock)); } TEST_F(KeyPerFileConfigurationTest, ShouldLoadNonExistingDirectory) { - auto provider = sb::cf::KeyPerFileConfigurationSource::create("nonexisting", true)->build(mock); + const auto provider = sb::cf::KeyPerFileConfigurationSource::create("nonexisting", true)->build(mock); provider->load(); @@ -38,50 +38,50 @@ TEST_F(KeyPerFileConfigurationTest, ShouldLoadNonExistingDirectory) TEST_F(KeyPerFileConfigurationTest, ShouldLoadDirectoryConfig) { - auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory")->build(mock); + const auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory")->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"settingOne", - {{"number", 12345}, - {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, - {"string", "string"}, - {"object", {{"num", 134}, {"string", "string"}}}}}, - {"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + const sb::cf::JsonObject expected = {{"settingOne", + {{"number", 12345}, + {"array", sb::cf::JsonArray{1, 2, 3, 4, 5, 6}}, + {"string", "string"}, + {"object", {{"num", 134}, {"string", "string"}}}}}, + {"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConfigFiles) { - auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory", false, "settingOne")->build(mock); + const auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory", false, "settingOne")->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + const sb::cf::JsonObject expected = {{"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConditionConfigFiles) { - auto provider = + const auto provider = sb::cf::KeyPerFileConfigurationSource::create("Directory", false, [](const std::filesystem::path &path) { return path.filename() == "settingOne.json"; })->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"settingTwo", - {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + const sb::cf::JsonObject expected = {{"settingTwo", + {{"array", sb::cf::JsonArray{1}}, + {"string", "stringdev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } diff --git a/Tests/Unit/Sources/MapConfigutationTest.cpp b/Tests/Unit/Sources/MapConfigutationTest.cpp index 5f42e98..4af1a90 100644 --- a/Tests/Unit/Sources/MapConfigutationTest.cpp +++ b/Tests/Unit/Sources/MapConfigutationTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "Mocks/ConfigurationBuilderMock.hpp" #include "SevenBit/Conf/Exceptions.hpp" @@ -29,16 +28,17 @@ TEST_F(MapConfigurationTest, ShouldFailProviderCreationDueToNullSource) TEST_F(MapConfigurationTest, ShouldMapSimpleConfiguration) { - auto provider = sb::cf::MapConfigurationSource::create(sb::cf::JsonConfigurationSource::create({{"yes", 12345}}), - [](sb::cf::JsonObject ob) { - ob["yes2"] = "yes"; - return std::move(ob); - }) - ->build(mock); + const auto provider = + sb::cf::MapConfigurationSource::create(sb::cf::JsonConfigurationSource::create({{"yes", 12345}}), + [](sb::cf::JsonObject ob) { + ob["yes2"] = "yes"; + return std::move(ob); + }) + ->build(mock); provider->load(); - sb::cf::JsonObject expected = {{"yes", 12345}, {"yes2", "yes"}}; + const sb::cf::JsonObject expected = {{"yes", 12345}, {"yes2", "yes"}}; EXPECT_EQ(provider->getConfiguration(), expected); } diff --git a/Tests/Unit/TestTemplate.cpp b/Tests/Unit/TestTemplate.cpp index a6fc38a..ab8f94f 100644 --- a/Tests/Unit/TestTemplate.cpp +++ b/Tests/Unit/TestTemplate.cpp @@ -1,5 +1,4 @@ #include -#include class Template : public testing::Test { @@ -12,7 +11,7 @@ class Template : public testing::Test void TearDown() override {} - ~Template() {} + ~Template() override = default; static void TearDownTestSuite() {} }; From 726599f80db909452adf21883b70fc488ef79c43 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 21:14:34 +0100 Subject: [PATCH 14/31] fix test key per file --- Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp b/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp index 9ca9754..9288ea1 100644 --- a/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp +++ b/Tests/Unit/Sources/KeyPerFileConfigurationTest.cpp @@ -49,8 +49,8 @@ TEST_F(KeyPerFileConfigurationTest, ShouldLoadDirectoryConfig) {"object", {{"num", 134}, {"string", "string"}}}}}, {"settingTwo", {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + {"string", "dev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "dev"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } @@ -63,8 +63,8 @@ TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConfigFiles) const sb::cf::JsonObject expected = {{"settingTwo", {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + {"string", "dev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "dev"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } @@ -80,8 +80,8 @@ TEST_F(KeyPerFileConfigurationTest, ShloudLoadFilteredConditionConfigFiles) const sb::cf::JsonObject expected = {{"settingTwo", {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + {"string", "dev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "dev"}}}}}}; EXPECT_EQ(provider->getConfiguration(), expected); } From 3dc2e9d828efbda244802a7a46b14b5ca617046e Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 21:18:21 +0100 Subject: [PATCH 15/31] fix test config --- Tests/Integration/ConfigurationTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Integration/ConfigurationTest.cpp b/Tests/Integration/ConfigurationTest.cpp index 7d95eaf..3e44401 100644 --- a/Tests/Integration/ConfigurationTest.cpp +++ b/Tests/Integration/ConfigurationTest.cpp @@ -40,8 +40,8 @@ TEST_F(ConfigurationTest, ShouldLoadConfig) {"object", {{"num", 134}, {"string", "string"}}}}}, {"settingTwo", {{"array", sb::cf::JsonArray{1}}, - {"string", "stringdev"}, - {"object", {{"inner", {{"num", 12345}}}, {"string", "stringdev"}}}}}}; + {"string", "dev"}, + {"object", {{"inner", {{"num", 12345}}}, {"string", "dev"}}}}}}; EXPECT_EQ(conf->root(), expected); } From 74993fce4e6c32aaa94a87cdea86c7fc116f6e79 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 22:43:07 +0100 Subject: [PATCH 16/31] update docs --- Examples/CommandLineParserConfig.cpp | 20 ++ ....cpp => CustomCommandLineParserConfig.cpp} | 0 ...ig.cpp => EnvironmentVarsParserConfig.cpp} | 8 +- README.md | 244 ++++++++++-------- 4 files changed, 164 insertions(+), 108 deletions(-) create mode 100644 Examples/CommandLineParserConfig.cpp rename Examples/{CustomSettingParser.cpp => CustomCommandLineParserConfig.cpp} (100%) rename Examples/{SettingParserConfig.cpp => EnvironmentVarsParserConfig.cpp} (76%) diff --git a/Examples/CommandLineParserConfig.cpp b/Examples/CommandLineParserConfig.cpp new file mode 100644 index 0000000..7ae4a1c --- /dev/null +++ b/Examples/CommandLineParserConfig.cpp @@ -0,0 +1,20 @@ +#include +#include + +using namespace sb::cf; + +int main(const int argc, char **argv) +{ + CommandLineParserConfig parserConfig; + parserConfig.optionPrefixes = {"//"}; + parserConfig.typeMarkers.clear(); + + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addCommandLine(argc, argv, std::move(parserConfig)) + .build(); + + std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; + + return 0; +} diff --git a/Examples/CustomSettingParser.cpp b/Examples/CustomCommandLineParserConfig.cpp similarity index 100% rename from Examples/CustomSettingParser.cpp rename to Examples/CustomCommandLineParserConfig.cpp diff --git a/Examples/SettingParserConfig.cpp b/Examples/EnvironmentVarsParserConfig.cpp similarity index 76% rename from Examples/SettingParserConfig.cpp rename to Examples/EnvironmentVarsParserConfig.cpp index 4d3240a..2f7f683 100644 --- a/Examples/SettingParserConfig.cpp +++ b/Examples/EnvironmentVarsParserConfig.cpp @@ -5,13 +5,13 @@ using namespace sb::cf; int main(const int argc, char **argv) { - EnvironmentVarsParserConfig envParserConfig; - envParserConfig.keySplitters.clear(); - envParserConfig.typeMarkers.clear(); + EnvironmentVarsParserConfig parserConfig; + parserConfig.keySplitters.clear(); + parserConfig.typeMarkers.clear(); const IConfiguration::Ptr configuration = ConfigurationBuilder{} // .addAppSettings() - .addEnvironmentVariables("", std::move(envParserConfig)) + .addEnvironmentVariables("", std::move(parserConfig)) .addCommandLine(argc, argv) .build(); diff --git a/README.md b/README.md index 0b3430f..aee0467 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,11 @@ - [Json Object](#json-object) - [In Memory](#in-memory) - [Custom Configuration Source](#custom-configuration-source) -- [Setting Parser Config](#setting-parser-config) - - [Usage Scenario](#usage-scenario) -- [Custom Settings Parser](#custom-setting-parser) +- [Command Line Parser Config](#command-line-parser-config) + - [Cmd Config Usage Scenario](#cmd-config-usage-scenario) +- [Environment Variables Parser Config](#environment-variables-parser-config) + - [Config Usage Scenario](#env-config-usage-scenario) +- [Custom Parsers](#custom-parsers) - [Advanced Usage Scenario](#advanced-usage-scenario) - [Build Library](#build-library) - [Build Library With Conan](#build-library-with-conan) @@ -79,15 +81,28 @@ The library is officially supported on the following platforms: ## Installation -All options, except Conan, require [taocpp json](https://github.com/taocpp/json) version 1.0.0-beta.14 to be already -installed. +### Using Cmake Fetch Content Api - Recommended -#### Using Conan Package Manager - Recommended +Update CMakeLists.txt file with the following code + +```cmake +include(FetchContent) +FetchContent_Declare( + 7bitDI + GIT_REPOSITORY https://github.com/7bitcoder/7bitConf.git + GIT_TAG v1.2.0 +) +FetchContent_MakeAvailable(7bitConf) + +target_link_libraries(Target 7bitConf::7bitConf) +``` + +### Using Conan Package Manager Download and install [Conan.io](https://conan.io/downloads.html) then install [package](https://conan.io/center/recipes/7bitconf), see Conan documentation for the package installation guide. -#### Header Only +### Header Only Download source code from the most recent release and copy the include folder into your project location, for example, copy into the '/SevenBitConf' folder. Include this folder into the project, example with [CMake](https://cmake.org/): @@ -96,12 +111,12 @@ copy into the '/SevenBitConf' folder. Include this folder into the project, exam include_directories(SevenBitConf/Include) ``` -#### Header Only Single File +### Header Only Single File Download SevenBitConf.hpp header file from the most recent release, copy this file into desired project location and include it. -#### Building Library Locally +### Building Library Locally Download source code from the most recent release, build or install the project using [CMake](https://cmake.org/), for more details, see the [Building Library](#build-library) guide. @@ -202,7 +217,7 @@ auto configuration = ConfigurationBuilder{}.addCommandLine(configArgs).build(); ``` The command line configuration source can be more customized with the additional addCommandLine method -arguments: [EnvironmentVarsParserConfig](#setting-parser-config) or [SettingsParser](#custom-setting-parser). +arguments: [CommandLineParserConfig](#command-line-parser-config) or [SettingParser](#custom-parsers). #### Supported Types @@ -253,8 +268,8 @@ is '\_\_' (double underscore) and for '!' is '\_\_\_' (triple underscore). Setting Array:2!uint=123 would be rewritten as Array\_\_2\_\_\_uint=123 Same as command line source, environment variables configuration source can be more customized with -additional addEnvironmentVariables method arguments: [EnvironmentVarsParserConfig](#setting-parser-config) -or [SettingsParser](#custom-setting-parser). +additional addEnvironmentVariables method arguments: [EnvironmentVarsParserConfig](#environment-variables-parser-config) +or [SettingParser](#custom-parsers). ### Json File @@ -412,17 +427,73 @@ int main(int argc, char **argv) } ``` -## Setting Parser Config +## Command Line Parser Config + +CommandLineParserConfig is a simple struct that contains the data used to configure the command line +parser, by default it is initialized with these values: + +```cpp +struct CommandLineParserConfig +{ + std::vector optionPrefixes = {"--", "/"}; + std::vector optionSplitters = {"=", " "}; + std::vector keySplitters = {":"}; + std::vector typeMarkers = {"!"}; + std::string_view defaultType = "string"; + bool throwOnUnknownType = true; + bool allowEmptyKeys = false; +}; +``` + +This configuration allows specifying different behaviors of the command line parser. -EnvironmentVarsParserConfig is a simple struct that contains the data used to configure the setting parser, by default -it is -initialized with these values: +Example option option:values:1!int=123 each part is marked as affected with () + +- optionPrefixes - list of possible option splitters option:values:1!int(=)123 +- optionSplitters - list of possible option splitters option:values:1!int(=)123 +- keySplitters - list of possible key splitters option(:)values(:)1!int=123 +- typeMarkers - list of possible type markers option:values:1(!)int=123 +- defaultType - is the type that is used if the type was not specified explicitly in variable option:values:1=123 +- throwOnUnknownType - if the type was not recognized in the parsing phase then an exception will be thrown if in this + case this setting is set to false default type will be used option:values:1!nonExistingType=123 +- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown option::1!int=123 + +### Cmd Config Usage Scenario + +All options should be loaded without considering the type and with custom option prefix '//', solution: + +```cpp +#include +#include + +using namespace sb::cf; + +int main(const int argc, char **argv) +{ + CommandLineParserConfig parserConfig; + parserConfig.optionPrefixes = {"//"}; + parserConfig.typeMarkers.clear(); + + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addCommandLine(argc, argv, std::move(parserConfig)) + .build(); + + std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; + + return 0; +} +``` + +## Environment Variables Parser Config + +EnvironmentVarsParserConfig is a similar struct to command line parser config with one difference +there is no way to set variable prefixes: ```cpp struct EnvironmentVarsParserConfig { - std::vector settingPrefixes = {"--"}; - std::vector settingSplitters = {"="}; + std::vector variableSplitters = {"="}; std::vector keySplitters = {":", "__"}; std::vector typeMarkers = {"!", "___"}; std::string_view defaultType = "string"; @@ -431,20 +502,19 @@ struct EnvironmentVarsParserConfig }; ``` -This configuration allows specifying different behaviors of the parser. +This configuration allows specifying different behaviors of the environment variable parser. -Example setting --option:values:1!int=123 each part is marked as affected with () +Example environment variable option__values__1___int=123 each part is marked as affected with () -- settingPrefixes - list of possible setting prefixes (--)option:values:1!int=123 -- settingSplitters - list of possible setting splitters --option:values:1!int(=)123 -- keySplitters - list of possible key splitters --option(:)values(:)1!int=123 -- typeMarkers - list of possible type markers --option:values:1(!)int=123 -- defaultType - is the type that is used if the type was not specified in setting --option:values:1=123 +- variableSplitters - list of possible variable splitters option__values__1___int(=)123 +- keySplitters - list of possible key splitters option(__)values(__)1___int=123 +- typeMarkers - list of possible type markers option__values__1(___)int=123 +- defaultType - is the type that is used if the type was not specified explicitly in variable option__values__1=123 - throwOnUnknownType - if the type was not recognized in the parsing phase then an exception will be thrown if in this - case this setting is set to false default type will be used --option:values:1!nonExistingType=123 -- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown --option::1!int=123 + case this setting is set to false default type will be used option__values__1___nonExistingType=123 +- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown option____1___int=123 -### Usage Scenario +### Env Config Usage Scenario All environment variables should be loaded as not nested objects (no key splitting) and without considering the type, solution: @@ -457,15 +527,15 @@ using namespace sb::cf; int main(int argc, char **argv) { - EnvironmentVarsParserConfig envParserConfig; - envParserConfig.keySplitters.clear(); - envParserConfig.typeMarkers.clear(); + EnvironmentVarsParserConfig parserConfig; + parserConfig.keySplitters.clear(); + parserConfig.typeMarkers.clear(); - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addAppSettings() - .addEnvironmentVariables("", std::move(envParserConfig)) - .addCommandLine(argc, argv) - .build(); + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addEnvironmentVariables("", std::move(parserConfig)) + .addCommandLine(argc, argv) + .build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; @@ -473,14 +543,11 @@ int main(int argc, char **argv) } ``` -In this case, custom EnvironmentVarsParserConfig is used in addEnvironmentVariables method, keySplitters is cleared to -prevent -extracting nested keys and typeMarkers is cleared to prevent type extraction. +## Custom Parsers -## Custom Setting Parser - -The library provides a SettingParserBuilder to create customized SettingParser, builder allows using custom value -deserializer, config, settingSplitter, and valueDeserializersMap. +The library provides a CommandLineParserBuilder and EnvironmentVarsParserBuilder to create customized parsers for +command line and environment variables, builder allows using custom value deserializer, config, settingSplitter, and +valueDeserializersMap. ### Advanced Usage Scenario @@ -497,28 +564,31 @@ using namespace sb::cf; struct MyTypeDeserializer final : IDeserializer { - JsonValue deserialize(std::optional value) const final { return value ? value : "emptyValue"; } + [[nodiscard]] JsonValue deserialize(std::optional value) const override + { + return value ? value : "emptyValue"; + } }; -int main(int argc, char **argv) +int main(const int argc, char **argv) { - EnvironmentVarsParserConfig envParserConfig; - envParserConfig.keySplitters.clear(); - envParserConfig.settingPrefixes.emplace_back("//"); - envParserConfig.defaultType = "myType"; - envParserConfig.throwOnUnknownType = false; - - ISettingParser::Ptr settingParser = SettingParserBuilder{} // - .useConfig(std::move(envParserConfig)) - .useDefaultValueDeserializers() - .useValueDeserializer("myType", std::make_unique()) - .build(); - - IConfiguration::Ptr configuration = ConfigurationBuilder{} // - .addAppSettings() - .addEnvironmentVariables() - .addCommandLine(argc, argv, std::move(settingParser)) - .build(); + auto builderFunc = [](CommandLineParserBuilder &builder) { + CommandLineParserConfig parserConfig; + parserConfig.keySplitters.clear(); + parserConfig.optionPrefixes.emplace_back("//"); + parserConfig.defaultType = "myType"; + parserConfig.throwOnUnknownType = false; + + builder.useConfig(std::move(parserConfig)) + .useDefaultValueDeserializers() + .useValueDeserializer("myType", std::make_unique()); + }; + + const IConfiguration::Ptr configuration = ConfigurationBuilder{} // + .addAppSettings() + .addEnvironmentVariables() + .addCommandLine(argc, argv, builderFunc) + .build(); std::cout << "Configuration json:" << std::endl << std::setw(2) << *configuration; @@ -528,15 +598,14 @@ int main(int argc, char **argv) In this case, keySplitters is cleared to prevent extracting nested keys, the additional setting prefix is added '//', and default type is changed to custom type "myType", and with throwOnUnknownType set to false instead of throwing an -exception when type is not recognized, default will be used. SettingParserBuilder is being used to create custom -settingParser with new config and custom valueDeserializer for type "myType", useDefaultValueDeserializers is used to -add predefined value deserializers (string, int, json ...). +exception when type is not recognized, default will be used. CommandLineParserBuilder is being used to create custom +comand line parser with new config and custom valueDeserializer for type "myType", useDefaultValueDeserializers is used +to add predefined value deserializers (string, int, json ...). ## Build Library The library can be built locally using [Cmake](https://cmake.org/), library -requires [Taocpp JSON](https://github.com/taocpp/json) to be already installed, the easiest way is -to [build a library with Conan](#build-library-with-conan). +uses [Taocpp JSON](https://github.com/taocpp/json) if library is not found it will be downloaded using cmake fetch api Create a build directory and navigate to it: @@ -554,9 +623,10 @@ Using this command, several cache variables can be set: - \: [possible values] (default value) - Description - \_7BIT_CONF_LIBRARY_TYPE: ["Shared", "Static", "HeaderOnly"] ("Static") - Library build type -- \_7BIT_CONF_BUILD_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build tests ( - requires [Gtest](https://google.github.io/googletest/) to be installed, - see [Build Library With Conan](#build-library-with-conan)) +- \_7BIT_CONF_BUILD_UNIT_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build unit tests +- \_7BIT_CONF_BUILD_INTEGRATION_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build integration tests +- \_7BIT_CONF_BUILD_E2E_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build e2e tests +- \_7BIT_CONF_BUILD_ALL_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build all tests - \_7BIT_CONF_BUILD_EXAMPLES: ["ON", "OFF"] ("OFF") - Turn on to build examples - \_7BIT_CONF_BUILD_SINGLE_HEADER: ["ON", "OFF"] ("OFF") - Turn on to build single header SevenBitConf.hpp (requires Quom to be installed) @@ -575,40 +645,6 @@ Build the library using the command: cmake --build . ``` -### Build Library With Conan - -Gtest library is added to the project using the Conan package -manager ([Conan Installation](https://conan.io/downloads.html)), -If Conan was freshly installed, run the detect command: - -```sh -conan profile detect -``` - -To install Conan packages, run this command in the library root folder: - -```sh -conan install . --output-folder=build --build=missing -``` - -Navigate to the build directory: - -```sh -cd build -``` - -Configure the CMake project and add the toolchain file as a CMAKE_TOOLCHAIN_FILE cache variable: - -```sh -cmake .. -DCMAKE_TOOLCHAIN_FILE:PATH="./conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release -D_7BIT_CONF_BUILD_TESTS=ON -``` - -Build the library using the command: - -```sh -cmake --build . -``` - ### Install Library To install the library, set the additional cache variables \_7BIT_CONF_INSTALL=ON and specify the installation directory From cbf3cc359ab867f71e549257d80eb12c29bb2699 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Thu, 22 Feb 2024 22:57:20 +0100 Subject: [PATCH 17/31] update e2e test --- Tests/E2E/echo_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/E2E/echo_test.py b/Tests/E2E/echo_test.py index 3896bb0..a74fe50 100644 --- a/Tests/E2E/echo_test.py +++ b/Tests/E2E/echo_test.py @@ -16,7 +16,7 @@ def get_echo_exec_path(): class EchoTest: def __init__(self, echo_exec_path): self.echoExecPath = echo_exec_path - self.testsData = self.__get_tests_data() + self.tests_data = self.__get_tests_data() @staticmethod def __get_tests_data(): @@ -45,9 +45,9 @@ def __run_test_and_summarize(self, test_data): return False def run(self): - all_tests = len(self.testsData) + all_tests = len(self.tests_data) succeeded_tests = 0 - for count, testData in enumerate(self.testsData, start=1): + for count, testData in enumerate(self.tests_data, start=1): print(f"Test {count}/{all_tests}") succeeded_tests += self.__run_test_and_summarize(testData) if succeeded_tests == all_tests: From 69d601c1e28b9ea0b3f0d934a715483fba36a2a2 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Fri, 23 Feb 2024 17:53:32 +0100 Subject: [PATCH 18/31] update documentation and optimize code --- .../SevenBit/Conf/IConfigurationBuilder.hpp | 11 ++- .../Sources/EnvironmentVarsConfiguration.hpp | 4 +- .../Sources/Impl/ChainedConfiguration.hpp | 3 +- .../Impl/EnvironmentVarsConfiguration.hpp | 6 +- .../Sources/Impl/KeyPerFileConfiguration.hpp | 3 +- README.md | 99 ++++++++++--------- Tests/E2E/echo.cpp | 3 +- Tests/Integration/ConfigurationTest.cpp | 1 - Tests/Unit/CommandLineParserBuilderTest.cpp | 1 - Tests/Unit/ConfigurationBuilderTest.cpp | 1 - Tests/Unit/Details/CommandLineParserTest.cpp | 1 - .../Details/EnvironmentVarsParserTest.cpp | 1 - Tests/Unit/Details/JsonExtTest.cpp | 1 - Tests/Unit/Details/SettingSplitterTest.cpp | 7 +- .../Details/ValueDeserializersMapTest.cpp | 1 - .../Unit/EnvironmentVarsParserBuilderTest.cpp | 1 - Tests/Unit/TestTemplate.cpp | 2 +- 17 files changed, 75 insertions(+), 71 deletions(-) diff --git a/Include/SevenBit/Conf/IConfigurationBuilder.hpp b/Include/SevenBit/Conf/IConfigurationBuilder.hpp index 5527ba8..e6c1922 100644 --- a/Include/SevenBit/Conf/IConfigurationBuilder.hpp +++ b/Include/SevenBit/Conf/IConfigurationBuilder.hpp @@ -77,8 +77,9 @@ namespace sb::cf IConfigurationBuilder &addEnvironmentVariables(std::string prefix, EnvironmentVarsParserConfig config) { - return addEnvironmentVariables(std::move(prefix), - [&](auto &builder) { builder.useConfig(std::move(config)); }); + return addEnvironmentVariables(std::move(prefix), [&](EnvironmentVarsParserBuilder &builder) { + builder.useConfig(std::move(config)); + }); } template @@ -106,12 +107,14 @@ namespace sb::cf IConfigurationBuilder &addCommandLine(int argc, char *const *const argv, CommandLineParserConfig config) { - return addCommandLine(argc, argv, [&](auto &builder) { builder.useConfig(std::move(config)); }); + return addCommandLine(argc, argv, + [&](CommandLineParserBuilder &builder) { builder.useConfig(std::move(config)); }); } IConfigurationBuilder &addCommandLine(std::vector args, CommandLineParserConfig config) { - return addCommandLine(std::move(args), [&](auto &builder) { builder.useConfig(std::move(config)); }); + return addCommandLine(std::move(args), + [&](CommandLineParserBuilder &builder) { builder.useConfig(std::move(config)); }); } template diff --git a/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp index d5f6b9b..f6207a3 100644 --- a/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/EnvironmentVarsConfiguration.hpp @@ -27,9 +27,9 @@ namespace sb::cf [[nodiscard]] static SPtr create(std::string prefix, ISettingsParser::Ptr parser); - [[nodiscard]] const std::string &getPrefix(); + [[nodiscard]] const std::string &getPrefix() const; - [[nodiscard]] const ISettingsParser &getSettingParser(); + [[nodiscard]] const ISettingsParser &getSettingParser() const; IConfigurationProvider::Ptr build(IConfigurationBuilder &builder) override; }; diff --git a/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp index 0c1f6c4..43bd24f 100644 --- a/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/ChainedConfiguration.hpp @@ -57,7 +57,8 @@ namespace sb::cf { details::Require::notNull(provider); provider->load(); - update(provider->getConfiguration()); + auto &optimized = const_cast(provider->getConfiguration()); + update(std::move(optimized)); } } diff --git a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp index f312ad0..c8d5b18 100644 --- a/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/EnvironmentVarsConfiguration.hpp @@ -30,9 +30,9 @@ namespace sb::cf new EnvironmentVarsConfigurationSource{std::move(prefix), std::move(parser)}); } - INLINE const std::string &EnvironmentVarsConfigurationSource::getPrefix() { return _prefix; } + INLINE const std::string &EnvironmentVarsConfigurationSource::getPrefix() const { return _prefix; } - INLINE const ISettingsParser &EnvironmentVarsConfigurationSource::getSettingParser() { return *_parser; } + INLINE const ISettingsParser &EnvironmentVarsConfigurationSource::getSettingParser() const { return *_parser; } INLINE IConfigurationProvider::Ptr EnvironmentVarsConfigurationSource::build(IConfigurationBuilder &builder) { @@ -54,7 +54,7 @@ namespace sb::cf auto &prefix = _source->getPrefix(); for (auto env = _7BIT_CONF_ENV_PTR; *env; env++) { - if (std::string_view envStr = *env; prefix.empty() || details::StringUtils::startsWith(envStr, prefix)) + if (std::string_view envStr = *env; details::StringUtils::startsWith(envStr, prefix)) { result.push_back(envStr.substr(prefix.size())); } diff --git a/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp index 00af0a8..1ed5047 100644 --- a/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp +++ b/Include/SevenBit/Conf/Sources/Impl/KeyPerFileConfiguration.hpp @@ -83,7 +83,8 @@ namespace sb::cf auto mapFcn = [name = filePath.stem().generic_string()](const JsonObject &config) -> JsonObject { auto res = JsonObject{}; const auto keys = details::StringUtils::split(name, "__"); - details::JsonExt::updateWith(res, keys, config); + auto &optimized = const_cast(config); + details::JsonExt::updateWith(res, keys, std::move(optimized)); return res; }; return MapConfigurationSource::create(std::move(fileSource), std::move(mapFcn)); diff --git a/README.md b/README.md index aee0467..ec33af7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![CI](https://github.com/7bitcoder/7bitConf/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/CI.yml) [![DevCI](https://github.com/7bitcoder/7bitConf/actions/workflows/DevCI.yml/badge.svg?branch=dev)](https://github.com/7bitcoder/7bitConf/actions/workflows/DevCI.yml) [![Windows](https://github.com/7bitcoder/7bitConf/actions/workflows/Windows.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/Windows.yml) [![Linux](https://github.com/7bitcoder/7bitConf/actions/workflows/Linux.yml/badge.svg?branch=main)](https://github.com/7bitcoder/7bitConf/actions/workflows/Linux.yml) @@ -8,7 +7,7 @@
logo -

C++17 configuration provider library!

+

C++17 centralized configuration provider library!

@@ -21,6 +20,7 @@ - [Built With](#built-with) - [Supported Platforms](#supported-platforms) - [Installation](#installation) + - [Using Cmake Fetch Content Api](#using-cmake-fetch-content-api---recommended) - [Using Conan Package Manager](#using-conan-package-manager) - [Header Only](#header-only) - [Header Only Single File](#header-only-single-file) @@ -88,7 +88,7 @@ Update CMakeLists.txt file with the following code ```cmake include(FetchContent) FetchContent_Declare( - 7bitDI + 7bitConf GIT_REPOSITORY https://github.com/7bitcoder/7bitConf.git GIT_TAG v1.2.0 ) @@ -113,12 +113,12 @@ include_directories(SevenBitConf/Include) ### Header Only Single File -Download SevenBitConf.hpp header file from the most recent release, copy this file into desired project location and -include it. +Download SevenBitConf.hpp header file from the most recent release, copy this file into the desired project location +and include it. ### Building Library Locally -Download source code from the most recent release, build or install the project using [CMake](https://cmake.org/), +Download source code from the most recent release, and build or install the project using [CMake](https://cmake.org/), for more details, see the [Building Library](#build-library) guide. ## Usage @@ -191,15 +191,14 @@ auto configuration = ConfigurationBuilder{}.addCommandLine(argc, argv).build(); **Argument patterns:** -* setting[:nestedSetting|arrayIndex...][!type]=[value] -* --setting[:nestedSetting|arrayIndex...][!type] [value] -* /setting[:nestedSetting|arrayIndex...][!type] [value] +- option[:nestedOption|arrayIndex...][!type]=[value] +- --option[:nestedOption|arrayIndex...][!type] [value] +- /option[:nestedOption|arrayIndex...][!type] [value] -Setting prefix '--' or '/' is optional when value is followed by '='. Nested settings are supported using -the ':' separator. If the object is an array, -numbers can be used to address the proper element. By default, setting values are saved as strings, but other types are -also supported using the '!' mark. If a value is not provided, the default one will be used for the specified type, -see [supported types](#supported-types). +Prefix ('--' or '/') is optional when the value is separated using '='. Nested settings are supported using the ':' +separator. If the object is an array, numbers can be used to address the proper element. By default, setting values are +saved as strings, but other types are also supported using the '!' mark. If a value is not provided, the default one +will be used for the specified type, see [supported types](#supported-types). Some arguments might be filtered using an overloaded method that accepts std::vector\ This example shows how to pass only arguments that start with "--SETTING": @@ -221,7 +220,7 @@ arguments: [CommandLineParserConfig](#command-line-parser-config) or [SettingPar #### Supported Types -type (default value) - Description +type (default value) - description - string ("") - default type, could be specified explicitly. - uint (0) - unsigned 64-bit integer. @@ -233,6 +232,7 @@ type (default value) - Description #### Example Command Line Arguments +- MySetting - will override or create a MySetting setting with the default "" string value. - MySetting="hello" - will override or create a MySetting setting with the "hello" string value. - Switch!bool=true - will override or create a Switch setting with a true bool value. - --MySetting hello - will override or create a MySetting setting with the "hello" string value. @@ -267,7 +267,7 @@ is '\_\_' (double underscore) and for '!' is '\_\_\_' (triple underscore). Setting Array:2!uint=123 would be rewritten as Array\_\_2\_\_\_uint=123 -Same as command line source, environment variables configuration source can be more customized with +Same as the command line source, the environment variables configuration source can be more customized with additional addEnvironmentVariables method arguments: [EnvironmentVarsParserConfig](#environment-variables-parser-config) or [SettingParser](#custom-parsers). @@ -447,20 +447,20 @@ struct CommandLineParserConfig This configuration allows specifying different behaviors of the command line parser. -Example option option:values:1!int=123 each part is marked as affected with () +Example option option:values:2!int=123 each part is marked as affected with () -- optionPrefixes - list of possible option splitters option:values:1!int(=)123 -- optionSplitters - list of possible option splitters option:values:1!int(=)123 -- keySplitters - list of possible key splitters option(:)values(:)1!int=123 -- typeMarkers - list of possible type markers option:values:1(!)int=123 -- defaultType - is the type that is used if the type was not specified explicitly in variable option:values:1=123 +- optionPrefixes - list of possible option prefixes: (--)option:values:2!int=123 +- optionSplitters - list of possible option splitters: --option:values:2!int(=)123 +- keySplitters - list of possible key splitters: --option(:)values(:)2!int=123 +- typeMarkers - list of possible type markers: --option:values:2(!)int=123 +- defaultType - is the type that is used if the type was not specified explicitly in a variable: --option:values:2=123 - throwOnUnknownType - if the type was not recognized in the parsing phase then an exception will be thrown if in this - case this setting is set to false default type will be used option:values:1!nonExistingType=123 -- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown option::1!int=123 + case this setting is set to false default type will be used: --option:values:2!nonExistingType=123 +- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown: --option::2!int=123 ### Cmd Config Usage Scenario -All options should be loaded without considering the type and with custom option prefix '//', solution: +All options should be loaded without considering the type and with the custom option prefix '//', solution: ```cpp #include @@ -487,8 +487,8 @@ int main(const int argc, char **argv) ## Environment Variables Parser Config -EnvironmentVarsParserConfig is a similar struct to command line parser config with one difference -there is no way to set variable prefixes: +EnvironmentVarsParserConfig is a similar struct to the command line parser config with one difference there is no way to +set variable prefixes: ```cpp struct EnvironmentVarsParserConfig @@ -504,15 +504,21 @@ struct EnvironmentVarsParserConfig This configuration allows specifying different behaviors of the environment variable parser. -Example environment variable option__values__1___int=123 each part is marked as affected with () +Example environment variable option\_\_values\_\_2\_\_\_int=123 each part is marked as affected with () -- variableSplitters - list of possible variable splitters option__values__1___int(=)123 -- keySplitters - list of possible key splitters option(__)values(__)1___int=123 -- typeMarkers - list of possible type markers option__values__1(___)int=123 -- defaultType - is the type that is used if the type was not specified explicitly in variable option__values__1=123 +- variableSplitters - list of possible variable splitters: option\_\_values\_\_2\_\_\_int(=)123 +- keySplitters - list of possible key splitters: option(\_\_)values(\_\_)2\_\_\_int=123 +- typeMarkers - list of possible type markers: option\_\_values\_\_2(\_\_\_)int=123 +- defaultType - is the type that is used if the type was not specified explicitly in a variable: optionvalues2=123 - throwOnUnknownType - if the type was not recognized in the parsing phase then an exception will be thrown if in this - case this setting is set to false default type will be used option__values__1___nonExistingType=123 -- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown option____1___int=123 + case this setting is set to false default type will be used: option\_\_values\_\_2\_\_\_nonExistingType=123 +- allowEmptyKeys - if set to true and empty keys are detected, an exception will be thrown: option\_\_\_\_2\_\_\_int=123 + +Some system environment variables are prefixed with double underscore \_\_, in that case, the environment parser by +default will throw an exception because this prefix will be treated as a key splitter resulting in the empty first part +of keys, example \_\_SOME_SYSTEM_ENV=value will be parsed to "" and "SOME_SYSTEM_ENV" keys, in that case, set +allowEmptyKeys to true or disable keys splitting with clearing keySplitters config value, or filter out unwanted +variables with prefix passed to addEnvironmentVariables method ### Env Config Usage Scenario @@ -545,14 +551,14 @@ int main(int argc, char **argv) ## Custom Parsers -The library provides a CommandLineParserBuilder and EnvironmentVarsParserBuilder to create customized parsers for -command line and environment variables, builder allows using custom value deserializer, config, settingSplitter, and -valueDeserializersMap. +The library provides a CommandLineParserBuilder and EnvironmentVarsParserBuilder builder classes to create customized +parsers for command line and environment variables, builder allows using custom value deserializer, config, +settingSplitter, and valueDeserializersMap. ### Advanced Usage Scenario -All command line options should be loaded as not nested objects (no key splitting) with consideration of new type -"myType" will set the option value to "emptyValue" string if no value was provided, myType should be used as the default +All command line options should be loaded as not nested objects (no key splitting) with consideration of the new type " +myType" will set the option value to "emptyValue" string if no value was provided, myType should be used as the default type, additional setting prefix should be considered '//' and default type should be also used if the type was not recognized, solution: @@ -597,15 +603,16 @@ int main(const int argc, char **argv) ``` In this case, keySplitters is cleared to prevent extracting nested keys, the additional setting prefix is added '//', -and default type is changed to custom type "myType", and with throwOnUnknownType set to false instead of throwing an -exception when type is not recognized, default will be used. CommandLineParserBuilder is being used to create custom -comand line parser with new config and custom valueDeserializer for type "myType", useDefaultValueDeserializers is used +and the default type is changed to custom type "myType", and with throwOnUnknownType set to false instead of throwing an +exception when type is not recognized, default will be used. CommandLineParserBuilder is being used to create a custom +command line parser with new config and custom valueDeserializer for type "myType", useDefaultValueDeserializers is used to add predefined value deserializers (string, int, json ...). ## Build Library The library can be built locally using [Cmake](https://cmake.org/), library -uses [Taocpp JSON](https://github.com/taocpp/json) if library is not found it will be downloaded using cmake fetch api +uses [Taocpp JSON](https://github.com/taocpp/json) if the library is not found it will be downloaded using Cmake fetch +api Create a build directory and navigate to it: @@ -626,14 +633,14 @@ Using this command, several cache variables can be set: - \_7BIT_CONF_BUILD_UNIT_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build unit tests - \_7BIT_CONF_BUILD_INTEGRATION_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build integration tests - \_7BIT_CONF_BUILD_E2E_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build e2e tests -- \_7BIT_CONF_BUILD_ALL_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build all tests +- \_7BIT_CONF_BUILD_ALL_TESTS: ["ON", "OFF"] ("OFF") - Turn on to build all tests (unit, integration and e2e) - \_7BIT_CONF_BUILD_EXAMPLES: ["ON", "OFF"] ("OFF") - Turn on to build examples - \_7BIT_CONF_BUILD_SINGLE_HEADER: ["ON", "OFF"] ("OFF") - Turn on to build single header SevenBitConf.hpp (requires Quom to be installed) - \_7BIT_CONF_INSTALL: ["ON", "OFF"] ("OFF") - Turn on to install the library -To set cache variable, pass additional option: -D\=[value], -for example, this command will set the library type to Static and will force examples built +To set the cache variable, pass the additional option: -D\=[value], for example, this command will +set the library type to Static and will force examples built ```sh cmake .. -DCMAKE_BUILD_TYPE=Release -D_7BIT_CONF_LIBRARY_TYPE=Static -D_7BIT_CONF_BUILD_EXAMPLES=true diff --git a/Tests/E2E/echo.cpp b/Tests/E2E/echo.cpp index 449e4eb..ef478d5 100644 --- a/Tests/E2E/echo.cpp +++ b/Tests/E2E/echo.cpp @@ -1,6 +1,7 @@ -#include <../../Include/SevenBit/Conf.hpp> #include +#include + using namespace sb::cf; int main(const int argc, char **argv) diff --git a/Tests/Integration/ConfigurationTest.cpp b/Tests/Integration/ConfigurationTest.cpp index 3e44401..7fc9f99 100644 --- a/Tests/Integration/ConfigurationTest.cpp +++ b/Tests/Integration/ConfigurationTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "SevenBit/Conf/ConfigurationBuilder.hpp" diff --git a/Tests/Unit/CommandLineParserBuilderTest.cpp b/Tests/Unit/CommandLineParserBuilderTest.cpp index 7e68dee..986e6d0 100644 --- a/Tests/Unit/CommandLineParserBuilderTest.cpp +++ b/Tests/Unit/CommandLineParserBuilderTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "Mocks/DeserializerMock.hpp" #include "Mocks/SettingSplitterMock.hpp" diff --git a/Tests/Unit/ConfigurationBuilderTest.cpp b/Tests/Unit/ConfigurationBuilderTest.cpp index 8c7830c..e4faafc 100644 --- a/Tests/Unit/ConfigurationBuilderTest.cpp +++ b/Tests/Unit/ConfigurationBuilderTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "Classes/CustomConfigSource.hpp" #include "SevenBit/Conf/ConfigurationBuilder.hpp" diff --git a/Tests/Unit/Details/CommandLineParserTest.cpp b/Tests/Unit/Details/CommandLineParserTest.cpp index 98204a5..d88275b 100644 --- a/Tests/Unit/Details/CommandLineParserTest.cpp +++ b/Tests/Unit/Details/CommandLineParserTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "Mocks/DeserializerMock.hpp" #include "Mocks/SettingSplitterMock.hpp" diff --git a/Tests/Unit/Details/EnvironmentVarsParserTest.cpp b/Tests/Unit/Details/EnvironmentVarsParserTest.cpp index 785093a..ab0f5eb 100644 --- a/Tests/Unit/Details/EnvironmentVarsParserTest.cpp +++ b/Tests/Unit/Details/EnvironmentVarsParserTest.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "Mocks/DeserializerMock.hpp" #include "Mocks/SettingSplitterMock.hpp" diff --git a/Tests/Unit/Details/JsonExtTest.cpp b/Tests/Unit/Details/JsonExtTest.cpp index 343d9bb..2cde0cb 100644 --- a/Tests/Unit/Details/JsonExtTest.cpp +++ b/Tests/Unit/Details/JsonExtTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "../../../Include/SevenBit/Conf/Details/JsonExt.hpp" #include "../../../Include/SevenBit/Conf/Exceptions.hpp" diff --git a/Tests/Unit/Details/SettingSplitterTest.cpp b/Tests/Unit/Details/SettingSplitterTest.cpp index 771894f..aab8c77 100644 --- a/Tests/Unit/Details/SettingSplitterTest.cpp +++ b/Tests/Unit/Details/SettingSplitterTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "../../../Include/SevenBit/Conf/Details/SettingSplitter.hpp" #include "../../../Include/SevenBit/Conf/Details/StringUtils.hpp" @@ -86,10 +85,10 @@ PARAMS_TEST_COMBINED_3(SettingSplitterTest, ShouldFailOnEmptyKeys, SettingSplitt { const auto &[settingSplitter, typeMarker, keySplitter] = GetParam(); - sb::cf::details::SettingSplitter splitter{{settingSplitter}, {typeMarker}, {keySplitter}, false}; + const sb::cf::details::SettingSplitter splitter{{settingSplitter}, {typeMarker}, {keySplitter}, false}; - auto key = sb::cf::details::StringUtils::join({"", "", ""}, keySplitter); - auto fullSetting = key + typeMarker + "type" + settingSplitter + "value"; + const auto key = sb::cf::details::StringUtils::join({"", "", ""}, keySplitter); + const auto fullSetting = key + typeMarker + "type" + settingSplitter + "value"; auto act = [&] { auto _ = splitter.split(fullSetting); }; EXPECT_THROW(act(), sb::cf::ConfigException); } diff --git a/Tests/Unit/Details/ValueDeserializersMapTest.cpp b/Tests/Unit/Details/ValueDeserializersMapTest.cpp index 45b4299..4d1dbc0 100644 --- a/Tests/Unit/Details/ValueDeserializersMapTest.cpp +++ b/Tests/Unit/Details/ValueDeserializersMapTest.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include "../../../Include/SevenBit/Conf/Details/Deserializers.hpp" diff --git a/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp b/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp index 731b180..e72c851 100644 --- a/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp +++ b/Tests/Unit/EnvironmentVarsParserBuilderTest.cpp @@ -1,5 +1,4 @@ #include -#include #include "Mocks/DeserializerMock.hpp" #include "Mocks/SettingSplitterMock.hpp" diff --git a/Tests/Unit/TestTemplate.cpp b/Tests/Unit/TestTemplate.cpp index ab8f94f..c8f294e 100644 --- a/Tests/Unit/TestTemplate.cpp +++ b/Tests/Unit/TestTemplate.cpp @@ -16,4 +16,4 @@ class Template : public testing::Test static void TearDownTestSuite() {} }; -TEST_F(Template, ExampleTest) {} +TEST_F(Template, ExampleTest) { EXPECT_TRUE(true); } From 6d0bf03d6ffc4123ba5786a34b5a67b9e37e7c8e Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 10:51:21 +0100 Subject: [PATCH 19/31] fix CI --- .github/workflows/Linux.yml | 12 ++++++++---- .github/workflows/MacOs.yml | 5 +++++ .github/workflows/Windows.yml | 7 +++++++ README.md | 2 +- Tests/Unit/Details/ValueDeserializersMapTest.cpp | 7 ++++--- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml index 8aa1198..c82f6f0 100644 --- a/.github/workflows/Linux.yml +++ b/.github/workflows/Linux.yml @@ -13,14 +13,14 @@ jobs: fail-fast: false matrix: compiler: - - { tool: gcc, ver: 7 } - { tool: gcc, ver: 8 } - { tool: gcc, ver: 9 } - { tool: gcc, ver: 10 } - { tool: gcc, ver: 11 } - { tool: gcc, ver: 12 } - { tool: gcc, ver: 13 } - - { tool: clang, ver: 7 } + - { tool: gcc, ver: 14 } + - { tool: gcc, ver: 15 } - { tool: clang, ver: 8 } - { tool: clang, ver: 9 } - { tool: clang, ver: 10 } @@ -29,6 +29,8 @@ jobs: - { tool: clang, ver: 13 } - { tool: clang, ver: 14 } - { tool: clang, ver: 15 } + - { tool: clang, ver: 16 } + - { tool: clang, ver: 17 } build_type: [ Release ] os: [ ubuntu-20.04, ubuntu-22.04 ] std: [ 17 ] @@ -45,12 +47,14 @@ jobs: exclude: - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 12 } } - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 13 } } + - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 14 } } + - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 15 } } - { os: ubuntu-20.04, compiler: { tool: clang, ver: 13 } } - { os: ubuntu-20.04, compiler: { tool: clang, ver: 14 } } - { os: ubuntu-20.04, compiler: { tool: clang, ver: 15 } } - - { os: ubuntu-22.04, compiler: { tool: gcc, ver: 7 } } + - { os: ubuntu-20.04, compiler: { tool: clang, ver: 16 } } + - { os: ubuntu-20.04, compiler: { tool: clang, ver: 17 } } - { os: ubuntu-22.04, compiler: { tool: gcc, ver: 8 } } - - { os: ubuntu-22.04, compiler: { tool: clang, ver: 7 } } - { os: ubuntu-22.04, compiler: { tool: clang, ver: 8 } } - { os: ubuntu-22.04, compiler: { tool: clang, ver: 9 } } - { os: ubuntu-22.04, compiler: { tool: clang, ver: 10 } } diff --git a/.github/workflows/MacOs.yml b/.github/workflows/MacOs.yml index f4cbeac..750d864 100644 --- a/.github/workflows/MacOs.yml +++ b/.github/workflows/MacOs.yml @@ -15,11 +15,14 @@ jobs: compiler: [ { tool: apple-clang }, + { tool: gcc, ver: 8 }, { tool: gcc, ver: 9 }, { tool: gcc, ver: 10 }, { tool: gcc, ver: 11 }, { tool: gcc, ver: 12 }, { tool: gcc, ver: 13 }, + { tool: gcc, ver: 14 }, + { tool: gcc, ver: 15 }, ] build_type: [ Release ] os: [ macos-12, macos-13 ] @@ -49,6 +52,8 @@ jobs: echo "CC=${{matrix.cc}}-${{matrix.compiler.ver}}" >> $GITHUB_ENV - name: Configure + env: + SYSTEM_VERSION_COMPAT: 1 run: cmake -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} -D_7BIT_CONF_LIBRARY_TYPE=${{matrix.library_type}} -D_7BIT_CONF_BUILD_ALL_TESTS=ON - name: Build diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index e6d19f5..7e65ff3 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -17,8 +17,12 @@ jobs: matrix: compiler: [ + { tool: msvc, ver: 140 }, { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, + { tool: msvc, ver: 143 }, + { tool: msvc, ver: 144 }, + { tool: msvc, ver: 145 }, { tool: mingw, ver: 7.5.0 }, { tool: mingw, ver: 8.5.0 }, { tool: mingw, ver: 9.4.0 }, @@ -26,6 +30,9 @@ jobs: { tool: mingw, ver: 11.2.0 }, { tool: mingw, ver: 12.2.0 }, { tool: mingw, ver: 13.2.0 }, + { tool: LLVM, ver: 8.0.1 }, + { tool: LLVM, ver: 9.0.1 }, + { tool: LLVM, ver: 10.0.0 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, diff --git a/README.md b/README.md index ec33af7..889d216 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ The library is officially supported on the following platforms: **Compilers:** - gcc 8.0+ -- clang 7.0+ +- clang 8.0+ - MSVC 2015+ ## Installation diff --git a/Tests/Unit/Details/ValueDeserializersMapTest.cpp b/Tests/Unit/Details/ValueDeserializersMapTest.cpp index 4d1dbc0..ace5b28 100644 --- a/Tests/Unit/Details/ValueDeserializersMapTest.cpp +++ b/Tests/Unit/Details/ValueDeserializersMapTest.cpp @@ -24,12 +24,13 @@ class ValueDeserializersMapTest : public testing::Test static void TearDownTestSuite() {} }; -sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap(const std::string_view defaultType = "string", - const bool throwOnUnknownType = true) +// inline fix for msvc build issue +inline sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap(const std::string_view defaultType = "string", + const bool throwOnUnknownType = true) { sb::cf::details::ValueDeserializersMap deserializers{defaultType, throwOnUnknownType}; sb::cf::details::DefaultDeserializers::add(deserializers); - return std::move(deserializers); + return deserializers; } static Params, sb::cf::JsonValue> DeserializeData = { From c9870b0e5712687e1ae9b2af4935fe9ff5ebcc2c Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 11:30:55 +0100 Subject: [PATCH 20/31] fix CI --- .github/workflows/Linux.yml | 8 ----- .github/workflows/MacOs.yml | 2 +- .github/workflows/Windows.yml | 3 +- Source/CMakeLists.txt | 31 ++++++++++--------- .../Details/ValueDeserializersMapTest.cpp | 20 ++++++------ 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml index c82f6f0..b558ab7 100644 --- a/.github/workflows/Linux.yml +++ b/.github/workflows/Linux.yml @@ -19,8 +19,6 @@ jobs: - { tool: gcc, ver: 11 } - { tool: gcc, ver: 12 } - { tool: gcc, ver: 13 } - - { tool: gcc, ver: 14 } - - { tool: gcc, ver: 15 } - { tool: clang, ver: 8 } - { tool: clang, ver: 9 } - { tool: clang, ver: 10 } @@ -29,8 +27,6 @@ jobs: - { tool: clang, ver: 13 } - { tool: clang, ver: 14 } - { tool: clang, ver: 15 } - - { tool: clang, ver: 16 } - - { tool: clang, ver: 17 } build_type: [ Release ] os: [ ubuntu-20.04, ubuntu-22.04 ] std: [ 17 ] @@ -47,13 +43,9 @@ jobs: exclude: - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 12 } } - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 13 } } - - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 14 } } - - { os: ubuntu-20.04, compiler: { tool: gcc, ver: 15 } } - { os: ubuntu-20.04, compiler: { tool: clang, ver: 13 } } - { os: ubuntu-20.04, compiler: { tool: clang, ver: 14 } } - { os: ubuntu-20.04, compiler: { tool: clang, ver: 15 } } - - { os: ubuntu-20.04, compiler: { tool: clang, ver: 16 } } - - { os: ubuntu-20.04, compiler: { tool: clang, ver: 17 } } - { os: ubuntu-22.04, compiler: { tool: gcc, ver: 8 } } - { os: ubuntu-22.04, compiler: { tool: clang, ver: 8 } } - { os: ubuntu-22.04, compiler: { tool: clang, ver: 9 } } diff --git a/.github/workflows/MacOs.yml b/.github/workflows/MacOs.yml index 750d864..8fb3303 100644 --- a/.github/workflows/MacOs.yml +++ b/.github/workflows/MacOs.yml @@ -53,7 +53,7 @@ jobs: - name: Configure env: - SYSTEM_VERSION_COMPAT: 1 + MACOSX_DEPLOYMENT_TARGET: 11 run: cmake -B ${{runner.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} -D_7BIT_CONF_LIBRARY_TYPE=${{matrix.library_type}} -D_7BIT_CONF_BUILD_ALL_TESTS=ON - name: Build diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 7e65ff3..4bb43bd 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -17,7 +17,6 @@ jobs: matrix: compiler: [ - { tool: msvc, ver: 140 }, { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, { tool: msvc, ver: 143 }, @@ -42,7 +41,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019 ] + os: [ windows-2019, windows-2022 ] std: [ 17 ] library_type: [ Static ] include: diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index cb4a212..6cdab6c 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -1,35 +1,36 @@ include(GenerateExportHeader) +set(PEGTL_USE_BOOST_FILESYSTEM OFF) find_package(taocpp-json QUIET) -if(NOT taocpp-json_FOUND) +if (NOT taocpp-json_FOUND) include(FetchContent) FetchContent_Declare( - taocpp-json - GIT_REPOSITORY https://github.com/taocpp/json.git - GIT_TAG 1.0.0-beta.14 - OVERRIDE_FIND_PACKAGE + taocpp-json + GIT_REPOSITORY https://github.com/taocpp/json.git + GIT_TAG 1.0.0-beta.14 + OVERRIDE_FIND_PACKAGE ) FetchContent_MakeAvailable(taocpp-json) -endif() +endif () find_package(taocpp-json REQUIRED) -if(_7BIT_CONF_SHARED_LIB) +if (_7BIT_CONF_SHARED_LIB) add_library(7bitConf SHARED Source.cpp) -elseif(_7BIT_CONF_STATIC_LIB) +elseif (_7BIT_CONF_STATIC_LIB) add_library(7bitConf STATIC Source.cpp) -elseif(_7BIT_CONF_HEADER_ONLY_LIB) +elseif (_7BIT_CONF_HEADER_ONLY_LIB) add_library(7bitConf INTERFACE) -endif() +endif () -if(_7BIT_CONF_HEADER_ONLY_LIB) +if (_7BIT_CONF_HEADER_ONLY_LIB) target_link_libraries(7bitConf INTERFACE taocpp-json) -else() +else () target_link_libraries(7bitConf taocpp-json) -endif() +endif () add_library(7bitConf::7bitConf ALIAS 7bitConf) @@ -38,5 +39,5 @@ set_target_properties(7bitConf PROPERTIES DEBUG_POSTFIX d) string(REPLACE ";" "$" dirs "${_7BIT_CONF_INCLUDE_DIR}") target_include_directories(7bitConf INTERFACE - $ - "$/${CMAKE_INSTALL_INCLUDEDIR}>") + $ + "$/${CMAKE_INSTALL_INCLUDEDIR}>") diff --git a/Tests/Unit/Details/ValueDeserializersMapTest.cpp b/Tests/Unit/Details/ValueDeserializersMapTest.cpp index ace5b28..c20608c 100644 --- a/Tests/Unit/Details/ValueDeserializersMapTest.cpp +++ b/Tests/Unit/Details/ValueDeserializersMapTest.cpp @@ -25,11 +25,11 @@ class ValueDeserializersMapTest : public testing::Test }; // inline fix for msvc build issue -inline sb::cf::details::ValueDeserializersMap makeDefaultDeserializersMap(const std::string_view defaultType = "string", - const bool throwOnUnknownType = true) +inline sb::cf::details::ValueDeserializersMap::Ptr makeDefaultDeserializersMap( + const std::string_view defaultType = "string", const bool throwOnUnknownType = true) { - sb::cf::details::ValueDeserializersMap deserializers{defaultType, throwOnUnknownType}; - sb::cf::details::DefaultDeserializers::add(deserializers); + auto deserializers = std::make_unique(defaultType, throwOnUnknownType); + sb::cf::details::DefaultDeserializers::add(*deserializers); return deserializers; } @@ -155,7 +155,7 @@ PARAMS_TEST(ValueDeserializersMapTest, ShouldDeserializeValue, DeserializeData) const auto &[type, value, expected] = GetParam(); const auto deserializers = makeDefaultDeserializersMap("string", false); - auto &deserializer = deserializers.getDeserializerFor(type); + auto &deserializer = deserializers->getDeserializerFor(type); EXPECT_EQ(deserializer.deserialize(value), expected); } @@ -163,7 +163,7 @@ TEST_F(ValueDeserializersMapTest, ShouldDeserializeEmptyJsonOption) { const auto deserializers = makeDefaultDeserializersMap(); - auto &deserializer = deserializers.getDeserializerFor("json"); + auto &deserializer = deserializers->getDeserializerFor("json"); EXPECT_EQ(deserializer.deserialize(std::nullopt), sb::cf::JsonValue{}); } @@ -171,10 +171,10 @@ TEST_F(ValueDeserializersMapTest, ShouldNotFoundDeserializer) { auto deserializers = makeDefaultDeserializersMap(); - deserializers.set("unknown2", nullptr); + deserializers->set("unknown2", nullptr); - auto get = [&](std::string_view type) { auto &_ = deserializers.getDeserializerFor(type); }; - EXPECT_EQ(deserializers.getDeserializersMap().size(), 8); + auto get = [&](std::string_view type) { auto &_ = deserializers->getDeserializerFor(type); }; + EXPECT_EQ(deserializers->getDeserializersMap().size(), 8); EXPECT_THROW(get("unknown"), sb::cf::ConfigException); EXPECT_THROW(get("unknown2"), sb::cf::ConfigException); } @@ -236,7 +236,7 @@ PARAMS_TEST(ValueDeserializersMapTest, ShouldFailDeserialize, FailDeserializeVal const auto &[type, value] = GetParam(); const auto deserializers = makeDefaultDeserializersMap(); - auto &deserializer = deserializers.getDeserializerFor(type); + auto &deserializer = deserializers->getDeserializerFor(type); EXPECT_ANY_THROW(auto result = deserializer.deserialize(value)); } From 8fc79a6588aedc111dbf86745516c2aba468e835 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:24:25 +0100 Subject: [PATCH 21/31] fix CI --- .github/workflows/MacOs.yml | 8 +------- .github/workflows/Windows.yml | 15 +-------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/.github/workflows/MacOs.yml b/.github/workflows/MacOs.yml index 8fb3303..ba03d30 100644 --- a/.github/workflows/MacOs.yml +++ b/.github/workflows/MacOs.yml @@ -15,17 +15,13 @@ jobs: compiler: [ { tool: apple-clang }, - { tool: gcc, ver: 8 }, - { tool: gcc, ver: 9 }, { tool: gcc, ver: 10 }, { tool: gcc, ver: 11 }, { tool: gcc, ver: 12 }, { tool: gcc, ver: 13 }, - { tool: gcc, ver: 14 }, - { tool: gcc, ver: 15 }, ] build_type: [ Release ] - os: [ macos-12, macos-13 ] + os: [ macos-12 ] std: [ 17 ] library_type: [ Static ] include: @@ -35,8 +31,6 @@ jobs: - compiler: { tool: apple-clang } cxx: "" cc: "" - exclude: - - { os: macos-12, compiler: { tool: gcc, ver: 9 } } runs-on: ${{matrix.os}} steps: diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 4bb43bd..07f5931 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -19,19 +19,6 @@ jobs: [ { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, - { tool: msvc, ver: 143 }, - { tool: msvc, ver: 144 }, - { tool: msvc, ver: 145 }, - { tool: mingw, ver: 7.5.0 }, - { tool: mingw, ver: 8.5.0 }, - { tool: mingw, ver: 9.4.0 }, - { tool: mingw, ver: 10.3.0 }, - { tool: mingw, ver: 11.2.0 }, - { tool: mingw, ver: 12.2.0 }, - { tool: mingw, ver: 13.2.0 }, - { tool: LLVM, ver: 8.0.1 }, - { tool: LLVM, ver: 9.0.1 }, - { tool: LLVM, ver: 10.0.0 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, @@ -41,7 +28,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019, windows-2022 ] + os: [ windows-2019 ] std: [ 17 ] library_type: [ Static ] include: From b67c807c4040d9b6cbaf43ada0c909ae3400f7a9 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:31:00 +0100 Subject: [PATCH 22/31] fix CI --- .github/workflows/MacOs.yml | 4 +++- .github/workflows/Windows.yml | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/MacOs.yml b/.github/workflows/MacOs.yml index ba03d30..a23a0e6 100644 --- a/.github/workflows/MacOs.yml +++ b/.github/workflows/MacOs.yml @@ -21,7 +21,7 @@ jobs: { tool: gcc, ver: 13 }, ] build_type: [ Release ] - os: [ macos-12 ] + os: [ macos-12, macos-13 ] std: [ 17 ] library_type: [ Static ] include: @@ -31,6 +31,8 @@ jobs: - compiler: { tool: apple-clang } cxx: "" cc: "" + exclude: + - { os: macos-13, compiler: { tool: gcc } } runs-on: ${{matrix.os}} steps: diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 07f5931..a2d7f13 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -19,6 +19,7 @@ jobs: [ { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, + { tool: msvc, ver: 143 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, @@ -28,7 +29,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019 ] + os: [ windows-2019, windows-2022 ] std: [ 17 ] library_type: [ Static ] include: @@ -44,6 +45,9 @@ jobs: cxx: "" cc: "" generator: "" + exclude: + - { os: windows-2022, compiler: { tool: LLVM } } + - { os: windows-2019, compiler: { tool: msvc, ver: 143 } } runs-on: ${{matrix.os}} steps: From cf938be9223a3215073a6b2e86333b15cc89d3ca Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:36:43 +0100 Subject: [PATCH 23/31] fix CI --- .github/workflows/Windows.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index a2d7f13..7e65ff3 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -17,9 +17,22 @@ jobs: matrix: compiler: [ + { tool: msvc, ver: 140 }, { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, { tool: msvc, ver: 143 }, + { tool: msvc, ver: 144 }, + { tool: msvc, ver: 145 }, + { tool: mingw, ver: 7.5.0 }, + { tool: mingw, ver: 8.5.0 }, + { tool: mingw, ver: 9.4.0 }, + { tool: mingw, ver: 10.3.0 }, + { tool: mingw, ver: 11.2.0 }, + { tool: mingw, ver: 12.2.0 }, + { tool: mingw, ver: 13.2.0 }, + { tool: LLVM, ver: 8.0.1 }, + { tool: LLVM, ver: 9.0.1 }, + { tool: LLVM, ver: 10.0.0 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, @@ -29,7 +42,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019, windows-2022 ] + os: [ windows-2019 ] std: [ 17 ] library_type: [ Static ] include: @@ -45,9 +58,6 @@ jobs: cxx: "" cc: "" generator: "" - exclude: - - { os: windows-2022, compiler: { tool: LLVM } } - - { os: windows-2019, compiler: { tool: msvc, ver: 143 } } runs-on: ${{matrix.os}} steps: From 4d72b30a2c5ae6dd9a55623900eee6f2e7b04409 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:38:19 +0100 Subject: [PATCH 24/31] fix Win CI --- .github/workflows/Windows.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 7e65ff3..c2b049a 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -21,18 +21,6 @@ jobs: { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, { tool: msvc, ver: 143 }, - { tool: msvc, ver: 144 }, - { tool: msvc, ver: 145 }, - { tool: mingw, ver: 7.5.0 }, - { tool: mingw, ver: 8.5.0 }, - { tool: mingw, ver: 9.4.0 }, - { tool: mingw, ver: 10.3.0 }, - { tool: mingw, ver: 11.2.0 }, - { tool: mingw, ver: 12.2.0 }, - { tool: mingw, ver: 13.2.0 }, - { tool: LLVM, ver: 8.0.1 }, - { tool: LLVM, ver: 9.0.1 }, - { tool: LLVM, ver: 10.0.0 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, @@ -42,7 +30,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019 ] + os: [ windows-2019, windows-2022 ] std: [ 17 ] library_type: [ Static ] include: From 3f5d676b0290c6ee4ceeb6986d17c2a413a481d3 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:39:03 +0100 Subject: [PATCH 25/31] fix Win CI --- .github/workflows/Windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index c2b049a..72ef67c 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -30,7 +30,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019, windows-2022 ] + os: [ windows-2019 ] std: [ 17 ] library_type: [ Static ] include: From f15b1783e0b9f369d0082e6c0b0ef3cbe8369661 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:39:39 +0100 Subject: [PATCH 26/31] fix Win CI --- .github/workflows/Windows.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 72ef67c..7e65ff3 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -21,6 +21,18 @@ jobs: { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, { tool: msvc, ver: 143 }, + { tool: msvc, ver: 144 }, + { tool: msvc, ver: 145 }, + { tool: mingw, ver: 7.5.0 }, + { tool: mingw, ver: 8.5.0 }, + { tool: mingw, ver: 9.4.0 }, + { tool: mingw, ver: 10.3.0 }, + { tool: mingw, ver: 11.2.0 }, + { tool: mingw, ver: 12.2.0 }, + { tool: mingw, ver: 13.2.0 }, + { tool: LLVM, ver: 8.0.1 }, + { tool: LLVM, ver: 9.0.1 }, + { tool: LLVM, ver: 10.0.0 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, From fda509bc9282b0fe183e52c46153f44e1ced359b Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:40:55 +0100 Subject: [PATCH 27/31] fix Win CI --- .github/workflows/Windows.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 7e65ff3..64c1194 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -7,9 +7,6 @@ on: - ".readthedocs.yaml" - "README.md" -env: - CHOCO_LIB_DIR: C:\\ProgramData\\chocolatey\\lib - jobs: test: strategy: @@ -23,13 +20,6 @@ jobs: { tool: msvc, ver: 143 }, { tool: msvc, ver: 144 }, { tool: msvc, ver: 145 }, - { tool: mingw, ver: 7.5.0 }, - { tool: mingw, ver: 8.5.0 }, - { tool: mingw, ver: 9.4.0 }, - { tool: mingw, ver: 10.3.0 }, - { tool: mingw, ver: 11.2.0 }, - { tool: mingw, ver: 12.2.0 }, - { tool: mingw, ver: 13.2.0 }, { tool: LLVM, ver: 8.0.1 }, { tool: LLVM, ver: 9.0.1 }, { tool: LLVM, ver: 10.0.0 }, @@ -68,11 +58,6 @@ jobs: shell: bash run: choco install ${{matrix.compiler.tool}} --version ${{matrix.compiler.ver}} --allow-downgrade -y && choco install ninja && cmake -E make_directory ${{runner.workspace}}/build - - name: Update Sys Path - if: matrix.compiler.tool == 'mingw' - run: ("${{env.CHOCO_LIB_DIR}}\\mingw\\tools\\install\\mingw64\\bin;" + (Get-Content -Path $env:GITHUB_PATH)) | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 - shell: pwsh - - name: Configure env: CXX: ${{matrix.cxx}} From d460ce917342d021b2879e532f7d0d43ba8b3261 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:41:14 +0100 Subject: [PATCH 28/31] fix Win CI --- .github/workflows/Windows.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 64c1194..fc1beb5 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -36,10 +36,6 @@ jobs: std: [ 17 ] library_type: [ Static ] include: - - compiler: { tool: mingw } - cxx: g++ - cc: gcc - generator: "MinGW Makefiles" - compiler: { tool: LLVM } cxx: clang++ cc: clang From 65509471cf2535776b9423ae0433cb52b7836cc4 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:41:51 +0100 Subject: [PATCH 29/31] fix Win CI --- .github/workflows/Windows.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index fc1beb5..5c9a8c0 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -32,7 +32,7 @@ jobs: { tool: LLVM, ver: 17.0.6 }, ] build_type: [ Release ] - os: [ windows-2019 ] + os: [ windows-2019, windows-2022 ] std: [ 17 ] library_type: [ Static ] include: @@ -44,6 +44,9 @@ jobs: cxx: "" cc: "" generator: "" + exclude: + - { os: windows-2022, compiler: { tool: LLVM } } + - { os: windows-2019, compiler: { tool: msvc, ver: 143 } } runs-on: ${{matrix.os}} steps: From 5321d9b9dc9d310268a113e4810616337706d240 Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:42:33 +0100 Subject: [PATCH 30/31] fix Win CI --- .github/workflows/Windows.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 5c9a8c0..91daacb 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -14,12 +14,9 @@ jobs: matrix: compiler: [ - { tool: msvc, ver: 140 }, { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, { tool: msvc, ver: 143 }, - { tool: msvc, ver: 144 }, - { tool: msvc, ver: 145 }, { tool: LLVM, ver: 8.0.1 }, { tool: LLVM, ver: 9.0.1 }, { tool: LLVM, ver: 10.0.0 }, From abd4cdaa317ef17e8f92163a4fd277a7a24de04e Mon Sep 17 00:00:00 2001 From: 7bitcoder Date: Sat, 24 Feb 2024 12:47:29 +0100 Subject: [PATCH 31/31] fix CI --- .github/workflows/Windows.yml | 3 --- Tests/Unit/Details/ValueDeserializersMapTest.cpp | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml index 91daacb..f92d04f 100644 --- a/.github/workflows/Windows.yml +++ b/.github/workflows/Windows.yml @@ -17,9 +17,6 @@ jobs: { tool: msvc, ver: 141 }, { tool: msvc, ver: 142 }, { tool: msvc, ver: 143 }, - { tool: LLVM, ver: 8.0.1 }, - { tool: LLVM, ver: 9.0.1 }, - { tool: LLVM, ver: 10.0.0 }, { tool: LLVM, ver: 11.1.0 }, { tool: LLVM, ver: 12.0.1 }, { tool: LLVM, ver: 13.0.1 }, diff --git a/Tests/Unit/Details/ValueDeserializersMapTest.cpp b/Tests/Unit/Details/ValueDeserializersMapTest.cpp index c20608c..68eace6 100644 --- a/Tests/Unit/Details/ValueDeserializersMapTest.cpp +++ b/Tests/Unit/Details/ValueDeserializersMapTest.cpp @@ -24,9 +24,8 @@ class ValueDeserializersMapTest : public testing::Test static void TearDownTestSuite() {} }; -// inline fix for msvc build issue -inline sb::cf::details::ValueDeserializersMap::Ptr makeDefaultDeserializersMap( - const std::string_view defaultType = "string", const bool throwOnUnknownType = true) +sb::cf::details::ValueDeserializersMap::Ptr makeDefaultDeserializersMap(const std::string_view defaultType = "string", + const bool throwOnUnknownType = true) { auto deserializers = std::make_unique(defaultType, throwOnUnknownType); sb::cf::details::DefaultDeserializers::add(*deserializers);