From c4132c0f5961fac85d4fd26e93888360f5f2581c Mon Sep 17 00:00:00 2001 From: "Dr. Colin Hirsch" Date: Thu, 4 Apr 2024 20:13:51 +0200 Subject: [PATCH] Test phase two order independence. --- .../tao/config/internal/phase2_everything.hpp | 2 +- src/test/config/CMakeLists.txt | 1 + src/test/config/failure.cpp | 2 +- src/test/config/independence.cpp | 107 ++++++++++++++++++ src/test/config/setenv.hpp | 32 ++++++ src/test/config/success.cpp | 22 +--- 6 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 src/test/config/independence.cpp create mode 100644 src/test/config/setenv.hpp diff --git a/include/tao/config/internal/phase2_everything.hpp b/include/tao/config/internal/phase2_everything.hpp index 1e5d5e6..2e16401 100644 --- a/include/tao/config/internal/phase2_everything.hpp +++ b/include/tao/config/internal/phase2_everything.hpp @@ -15,7 +15,7 @@ namespace tao::config::internal { [[nodiscard]] inline bool phase2_iteration( state& st, const function_map& fm ) { - return ( phase2_functions( st, fm ) + phase2_additions( st.root ) + phase2_references( st.root ) + phase2_asterisks( st.root ) ) > 0; + return ( phase2_functions( st, fm ) | phase2_additions( st.root ) | phase2_references( st.root ) | phase2_asterisks( st.root ) ) > 0; } inline void phase2_everything( state& st, const function_map& fm ) diff --git a/src/test/config/CMakeLists.txt b/src/test/config/CMakeLists.txt index 8b5390e..3f0571d 100644 --- a/src/test/config/CMakeLists.txt +++ b/src/test/config/CMakeLists.txt @@ -7,6 +7,7 @@ set(testsources debug_traits.cpp enumerations.cpp failure.cpp + independence.cpp key.cpp key_part.cpp multi_line_string_position.cpp diff --git a/src/test/config/failure.cpp b/src/test/config/failure.cpp index 54b9571..3780948 100644 --- a/src/test/config/failure.cpp +++ b/src/test/config/failure.cpp @@ -51,7 +51,7 @@ int main() } } if( tao::failed == 0 ) { - std::cerr << "All " << count << " testcases passed." << std::endl; + std::cerr << "All " << count << " failure testcases passed." << std::endl; } return std::min( int( tao::failed ), 127 ); } diff --git a/src/test/config/independence.cpp b/src/test/config/independence.cpp new file mode 100644 index 0000000..db5fb07 --- /dev/null +++ b/src/test/config/independence.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#include +#include +#include +#include +#include +#include + +#include "setenv.hpp" +#include "test.hpp" + +#include + +namespace tao::config +{ + std::size_t iteration = 0; + + [[nodiscard]] std::size_t p2fun( internal::state& st, const internal::function_map& fm ) + { + return internal::phase2_functions( st, fm ); + } + + [[nodiscard]] std::size_t p2add( internal::state& st, const internal::function_map& /*unused*/ ) + { + return internal::phase2_additions( st.root ); + } + + [[nodiscard]] std::size_t p2ref( internal::state& st, const internal::function_map& /*unused*/ ) + { + return internal::phase2_references( st.root ); + } + + [[nodiscard]] std::size_t p2ast( internal::state& st, const internal::function_map& /*unused*/ ) + { + return internal::phase2_asterisks( st.root ); + } + + using func_t = std::size_t( * )( internal::state&, const internal::function_map& ); + + std::set< func_t > p2funcs = { p2fun, p2add, p2ref, p2ast }; + + void unit_test( const std::filesystem::path& path ) + { + try { + std::filesystem::path jaxn = path; + jaxn.replace_extension( ".jaxn" ); + const auto cj = from_file( jaxn ); + const auto cjs = json::jaxn::to_string( cj ); + + std::vector< func_t > v( p2funcs.begin(), p2funcs.end() ); + + do { + internal::config_parser p; + p.parse( path ); + + while( v[ 0 ]( p.st, p.fm ) | v[ 1 ]( p.st, p.fm ) | v[ 2 ]( p.st, p.fm ) | v[ 3 ]( p.st, p.fm ) ); + + internal::phase3_remove( p.st.root ); + const auto cc = internal::phase5_repack< traits >( p.st.root ); + const auto ccs = json::jaxn::to_string( cc ); + + if( ccs != cjs ) { + // LCOV_EXCL_START + ++failed; + std::cerr << std::endl + << "Testcase '" << path << "' failed identity test in iteration " << iteration << std::endl; + std::cerr << "<<< Config parsed as config <<<" << std::endl; + std::cerr << ccs << std::endl; + std::cerr << ">>> Config parsed as config >>>" << std::endl; + std::cerr << "<<< Reference data parsed as config <<<" << std::endl; + std::cerr << cjs << std::endl; + std::cerr << ">>> Reference data parsed as config >>>" << std::endl; + // LCOV_EXCL_STOP + } + ++iteration; + } while( std::next_permutation( v.begin(), v.end() ) ); + } + // LCOV_EXCL_START + catch( const std::exception& e ) { + std::cerr << "Testcase '" << path << "' failed with exception '" << e.what() << "' in iteration " << iteration << std::endl; + ++failed; + } + // LCOV_EXCL_STOP + } + +} // namespace tao::config + +int main() +{ + for( const auto& entry : std::filesystem::directory_iterator( "tests" ) ) { + if( const auto& path = entry.path(); path.extension() == ".success" ) { +#if defined( _MSC_VER ) + if( entry.path().stem() == "shell" ) { + continue; + } +#endif + tao::config::internal::setenv_throws( "TAO_CONFIG", "env_value" ); + tao::config::unit_test( path ); + } + } + if( tao::config::failed == 0 ) { + std::cerr << "All " << tao::config::iteration << " order independence testcases passed." << std::endl; + } + return std::min( int( tao::config::failed ), 127 ); +} diff --git a/src/test/config/setenv.hpp b/src/test/config/setenv.hpp new file mode 100644 index 0000000..47395f9 --- /dev/null +++ b/src/test/config/setenv.hpp @@ -0,0 +1,32 @@ +// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_SRC_TEST_CONFIG_SETENV_HPP +#define TAO_CONFIG_SRC_TEST_CONFIG_SETENV_HPP + +#include +#include +#include + +namespace tao::config::internal +{ + void setenv_throws( const std::string& name, const std::string& value ) + { +#if defined( _MSC_VER ) + const auto e = ::_putenv_s( name.c_str(), value.c_str() ); + if( e != 0 ) { +#else + errno = 0; + if( ::setenv( name.c_str(), value.c_str(), 1 ) != 0 ) { + // LCOV_EXCL_START + const auto e = errno; +#endif + (void)e; + throw std::runtime_error( "setenv failed" ); + // LCOV_EXCL_STOP + } + } + +} // namespace tao::config::internal + +#endif diff --git a/src/test/config/success.cpp b/src/test/config/success.cpp index d1c1a42..20ef8bd 100644 --- a/src/test/config/success.cpp +++ b/src/test/config/success.cpp @@ -6,29 +6,13 @@ #include #include +#include "setenv.hpp" #include "test.hpp" #include namespace tao::config { - void setenv_throws( const std::string& name, const std::string& value ) - { -#if defined( _MSC_VER ) - const auto e = ::_putenv_s( name.c_str(), value.c_str() ); - if( e != 0 ) { -#else - errno = 0; - if( ::setenv( name.c_str(), value.c_str(), 1 ) != 0 ) { - // LCOV_EXCL_START - const auto e = errno; -#endif - (void)e; - throw std::runtime_error( "setenv failed" ); - // LCOV_EXCL_STOP - } - } - template< template< typename... > class Traits > void unit_test( const std::filesystem::path& path ) { @@ -119,14 +103,14 @@ int main() continue; } #endif - tao::config::setenv_throws( "TAO_CONFIG", "env_value" ); + tao::config::internal::setenv_throws( "TAO_CONFIG", "env_value" ); tao::config::unit_test< tao::json::traits >( path ); tao::config::unit_test< tao::config::traits >( path ); ++count; } } if( tao::config::failed == 0 ) { - std::cerr << "All " << count << " testcases passed." << std::endl; + std::cerr << "All " << count << " success testcases passed." << std::endl; } return std::min( int( tao::config::failed ), 127 ); }