From 43428c609332fcf270cc3c3ada28b2c1427d8e2a Mon Sep 17 00:00:00 2001 From: Phil nash Date: Thu, 4 Apr 2019 15:55:46 +0100 Subject: [PATCH] First commit of STRINGIFY_ENUM --- .../internal/catch_enum_values_registry.cpp | 68 +++++++ include/internal/catch_enum_values_registry.h | 32 ++++ .../catch_interfaces_enum_values_registry.h | 40 ++++ .../internal/catch_interfaces_registry_hub.h | 4 +- include/internal/catch_registry_hub.cpp | 5 + include/internal/catch_string_manip.cpp | 17 ++ include/internal/catch_string_manip.h | 3 + include/internal/catch_tostring.h | 14 ++ projects/CMakeLists.txt | 3 + .../Baselines/compact.sw.approved.txt | 22 +++ .../Baselines/console.std.approved.txt | 4 +- .../Baselines/console.sw.approved.txt | 137 +++++++++++++- .../SelfTest/Baselines/junit.sw.approved.txt | 8 +- .../SelfTest/Baselines/xml.sw.approved.txt | 173 +++++++++++++++++- .../IntrospectiveTests/String.tests.cpp | 11 ++ .../UsageTests/EnumToString.tests.cpp | 54 ++++++ 16 files changed, 587 insertions(+), 8 deletions(-) create mode 100644 include/internal/catch_enum_values_registry.cpp create mode 100644 include/internal/catch_enum_values_registry.h create mode 100644 include/internal/catch_interfaces_enum_values_registry.h diff --git a/include/internal/catch_enum_values_registry.cpp b/include/internal/catch_enum_values_registry.cpp new file mode 100644 index 00000000..e974a67d --- /dev/null +++ b/include/internal/catch_enum_values_registry.cpp @@ -0,0 +1,68 @@ +/* + * Created by Phil on 4/4/2019. + * Copyright 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#include "catch_enum_values_registry.h" +#include "catch_string_manip.h" +#include "catch_stream.h" + +#include + +namespace Catch { + + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + IEnumInfo::~IEnumInfo() {} + + namespace Detail { + + std::vector parseEnums( StringRef enums ) { + auto enumValues = splitString( enums, ',' ); + std::vector parsed; + parsed.reserve( enumValues.size() ); + for( auto const& enumValue : enumValues ) { + auto identifiers = splitString( enumValue, ':' ); + parsed.push_back( Catch::trim( identifiers.back() ) ); + } + return parsed; + } + + struct EnumInfo : IEnumInfo { + std::string m_name; + std::map m_values; + + ~EnumInfo(); + + std::string lookup( int value ) const override { + auto it = m_values.find( value ); + if( it == m_values.end() ) { + ReusableStringStream rss; + rss << "{** unexpected value for " << m_name << ": " << value << "**}"; + return rss.str(); + } + return it->second; + } + }; + EnumInfo::~EnumInfo() {} + + IEnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values ) { + std::unique_ptr enumInfo( new EnumInfo ); + enumInfo->m_name = enumName; + + const auto valueNames = Catch::Detail::parseEnums( allValueNames ); + assert( valueNames.size() == values.size() ); + std::size_t i = 0; + for( auto value : values ) + enumInfo->m_values.insert({ value, valueNames[i++] }); + + EnumInfo* raw = enumInfo.get(); + m_enumInfos.push_back( std::move( enumInfo ) ); + return *raw; + } + + } // Detail + +} // Catch + diff --git a/include/internal/catch_enum_values_registry.h b/include/internal/catch_enum_values_registry.h new file mode 100644 index 00000000..8103bb9d --- /dev/null +++ b/include/internal/catch_enum_values_registry.h @@ -0,0 +1,32 @@ +/* + * Created by Phil on 4/4/2019. + * Copyright 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED +#define TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED + +#include "catch_interfaces_enum_values_registry.h" + +#include + +namespace Catch { + + namespace Detail { + + class EnumValuesRegistry : public IMutableEnumValuesRegistry { + + std::vector> m_enumInfos; + + IEnumInfo const& registerEnum(StringRef enumName, StringRef allEnums, std::vector const& values) override; + }; + + std::vector parseEnums( StringRef enums ); + + } // Detail + +} // Catch + +#endif //TWOBLUECUBES_CATCH_ENUMVALUESREGISTRY_H_INCLUDED \ No newline at end of file diff --git a/include/internal/catch_interfaces_enum_values_registry.h b/include/internal/catch_interfaces_enum_values_registry.h new file mode 100644 index 00000000..b94ee69b --- /dev/null +++ b/include/internal/catch_interfaces_enum_values_registry.h @@ -0,0 +1,40 @@ +/* + * Created by Phil on 4/4/2019. + * Copyright 2019 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED +#define TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED + +#include "catch_stringref.h" + +#include + +namespace Catch { + + struct IEnumInfo { + virtual ~IEnumInfo(); + + virtual std::string lookup( int value ) const = 0; + }; + + struct IMutableEnumValuesRegistry { + virtual ~IMutableEnumValuesRegistry(); + + virtual IEnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; + + template + IEnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { + std::vector intValues; + intValues.reserve( values.size() ); + for( auto enumValue : values ) + intValues.push_back( static_cast( enumValue ) ); + return registerEnum( enumName, allEnums, intValues ); + } + }; + +} // Catch + +#endif //TWOBLUECUBES_CATCH_INTERFACESENUMVALUESREGISTRY_H_INCLUDED diff --git a/include/internal/catch_interfaces_registry_hub.h b/include/internal/catch_interfaces_registry_hub.h index 8e1da61e..19ffbf26 100644 --- a/include/internal/catch_interfaces_registry_hub.h +++ b/include/internal/catch_interfaces_registry_hub.h @@ -22,6 +22,8 @@ namespace Catch { struct IReporterRegistry; struct IReporterFactory; struct ITagAliasRegistry; + struct IMutableEnumValuesRegistry; + class StartupExceptionRegistry; using IReporterFactoryPtr = std::shared_ptr; @@ -32,7 +34,6 @@ namespace Catch { virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; - virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; @@ -47,6 +48,7 @@ namespace Catch { virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; }; IRegistryHub const& getRegistryHub(); diff --git a/include/internal/catch_registry_hub.cpp b/include/internal/catch_registry_hub.cpp index a5062398..8a3c7a97 100644 --- a/include/internal/catch_registry_hub.cpp +++ b/include/internal/catch_registry_hub.cpp @@ -15,6 +15,7 @@ #include "catch_tag_alias_registry.h" #include "catch_startup_exception_registry.h" #include "catch_singletons.hpp" +#include "catch_enum_values_registry.h" namespace Catch { @@ -60,6 +61,9 @@ namespace Catch { void registerStartupException() noexcept override { m_exceptionRegistry.add(std::current_exception()); } + IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; + } private: TestRegistry m_testCaseRegistry; @@ -67,6 +71,7 @@ namespace Catch { ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; TagAliasRegistry m_tagAliasRegistry; StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; }; } diff --git a/include/internal/catch_string_manip.cpp b/include/internal/catch_string_manip.cpp index 904d1013..60109335 100644 --- a/include/internal/catch_string_manip.cpp +++ b/include/internal/catch_string_manip.cpp @@ -6,11 +6,13 @@ */ #include "catch_string_manip.h" +#include "catch_stringref.h" #include #include #include #include +#include namespace Catch { @@ -65,6 +67,21 @@ namespace Catch { return replaced; } + std::vector splitString( StringRef str, char delimiter ) { + std::vector subStrings; + std::size_t start = 0; + for(std::size_t pos = 0; pos < str.size(); ++pos ) { + if( str[pos] == delimiter ) { + if( pos - start > 1 ) + subStrings.push_back( str.substr( start, pos-start ) ); + start = pos+1; + } + } + if( start < str.size() ) + subStrings.push_back( str.substr( start, str.size()-start ) ); + return subStrings; + } + pluralise::pluralise( std::size_t count, std::string const& label ) : m_count( count ), m_label( label ) diff --git a/include/internal/catch_string_manip.h b/include/internal/catch_string_manip.h index 6292cd57..0f8176d6 100644 --- a/include/internal/catch_string_manip.h +++ b/include/internal/catch_string_manip.h @@ -12,6 +12,8 @@ namespace Catch { + class StringRef; + bool startsWith( std::string const& s, std::string const& prefix ); bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); @@ -21,6 +23,7 @@ namespace Catch { std::string toLower( std::string const& s ); std::string trim( std::string const& str ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + std::vector splitString( StringRef str, char delimiter ); struct pluralise { pluralise( std::size_t count, std::string const& label ); diff --git a/include/internal/catch_tostring.h b/include/internal/catch_tostring.h index 13a43b0c..dc236dea 100644 --- a/include/internal/catch_tostring.h +++ b/include/internal/catch_tostring.h @@ -15,6 +15,7 @@ #include #include "catch_compiler_capabilities.h" #include "catch_stream.h" +#include "catch_interfaces_enum_values_registry.h" #ifdef CATCH_CONFIG_CPP17_STRING_VIEW #include @@ -639,6 +640,19 @@ struct ratio_string { } #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#define INTERNAL_CATCH_STRINGIFY_ENUM( enumName, ... ) \ + template<> struct ::Catch::StringMaker { \ + static std::string convert( enumName value ) { \ + static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ + return enumInfo.lookup( static_cast( value ) ); \ + } \ + }; + +#ifdef CATCH_CONFIG_PREFIX_ALL +# define CATCH_STRINGIFY_ENUM( enumName, ... ) INTERNAL_CATCH_STRINGIFY_ENUM( enumName, __VA_ARGS__ ) +#else +# define STRINGIFY_ENUM( enumName, ... ) INTERNAL_CATCH_STRINGIFY_ENUM( enumName, __VA_ARGS__ ) +#endif #ifdef _MSC_VER #pragma warning(pop) diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 37b0865a..36263636 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -97,6 +97,7 @@ set(INTERNAL_HEADERS ${HEADER_DIR}/internal/catch_decomposer.h ${HEADER_DIR}/internal/catch_default_main.hpp ${HEADER_DIR}/internal/catch_enforce.h + ${HEADER_DIR}/internal/catch_enum_values_registry.h ${HEADER_DIR}/internal/catch_errno_guard.h ${HEADER_DIR}/internal/catch_exception_translator_registry.h ${HEADER_DIR}/internal/catch_external_interfaces.h @@ -107,6 +108,7 @@ set(INTERNAL_HEADERS ${HEADER_DIR}/internal/catch_impl.hpp ${HEADER_DIR}/internal/catch_interfaces_capture.h ${HEADER_DIR}/internal/catch_interfaces_config.h + ${HEADER_DIR}/internal/catch_interfaces_enum_values_registry.h ${HEADER_DIR}/internal/catch_interfaces_exception.h ${HEADER_DIR}/internal/catch_interfaces_registry_hub.h ${HEADER_DIR}/internal/catch_interfaces_reporter.h @@ -182,6 +184,7 @@ set(IMPL_SOURCES ${HEADER_DIR}/internal/catch_debugger.cpp ${HEADER_DIR}/internal/catch_decomposer.cpp ${HEADER_DIR}/internal/catch_enforce.cpp + ${HEADER_DIR}/internal/catch_enum_values_registry.cpp ${HEADER_DIR}/internal/catch_errno_guard.cpp ${HEADER_DIR}/internal/catch_exception_translator_registry.cpp ${HEADER_DIR}/internal/catch_fatal_condition.cpp diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt index 7c60b2af..87674a4b 100644 --- a/projects/SelfTest/Baselines/compact.sw.approved.txt +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -298,6 +298,11 @@ Approx.tests.cpp:: passed: 101.000001 != Approx(100).epsilon(0.01) Approx.tests.cpp:: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 ) Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith("Substring") for: "this string contains 'abc' as a substring" ends with: "Substring" Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive) +EnumToString.tests.cpp:: passed: enumInfo.lookup(0) == "Value1" for: "Value1" == "Value1" +EnumToString.tests.cpp:: passed: enumInfo.lookup(1) == "Value2" for: "Value2" == "Value2" +EnumToString.tests.cpp:: passed: enumInfo.lookup(3) == "{** unexpected value for EnumName: 3**}" for: "{** unexpected value for EnumName: 3**}" +== +"{** unexpected value for EnumName: 3**}" Approx.tests.cpp:: passed: 101.01 != Approx(100).epsilon(0.01) for: 101.01 != Approx( 100.0 ) Condition.tests.cpp:: failed: data.int_seven == 6 for: 7 == 6 Condition.tests.cpp:: failed: data.int_seven == 8 for: 7 == 8 @@ -912,6 +917,13 @@ Matchers.tests.cpp:: failed: testStringForMatching(), Matches("this Matchers.tests.cpp:: failed: testStringForMatching(), Matches("contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively Matchers.tests.cpp:: failed: testStringForMatching(), Matches("this string contains 'abc' as a") for: "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively Matchers.tests.cpp:: passed: actual, !UnorderedEquals(expected) for: { 'a', 'b' } not UnorderedEquals: { 'c', 'b' } +EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1" +EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value2 ) == "Value2" for: "Value2" == "Value2" +EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value3 ) == "Value3" for: "Value3" == "Value3" +EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value4 ) == "{** unexpected value for EnumClass3: 3**}" for: "{** unexpected value for EnumClass3: 3**}" +== +"{** unexpected value for EnumClass3: 3**}" +EnumToString.tests.cpp:: passed: stringify( ec3 ) == "Value2" for: "Value2" == "Value2" Message.tests.cpp:: passed: with 1 message: 'this is a success' Message.tests.cpp:: passed: BDD.tests.cpp:: passed: before == 0 for: 0 == 0 @@ -1352,6 +1364,13 @@ Tricky.tests.cpp:: passed: ptr.get() == 0 for: 0 == 0 ToStringPair.tests.cpp:: passed: ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }" for: "{ { 42, "Arthur" }, { "Ford", 24 } }" == "{ { 42, "Arthur" }, { "Ford", 24 } }" +EnumToString.tests.cpp:: passed: parseEnums( "" ), Equals( std::vector{} ) for: { } Equals: { } +EnumToString.tests.cpp:: passed: parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector{"Value1"} ) for: { "Value1" } Equals: { "Value1" } +EnumToString.tests.cpp:: passed: parseEnums( "Value1" ), Equals( std::vector{"Value1"} ) for: { "Value1" } Equals: { "Value1" } +EnumToString.tests.cpp:: passed: parseEnums( "EnumName::Value1" ), Equals(std::vector{"Value1"} ) for: { "Value1" } Equals: { "Value1" } +EnumToString.tests.cpp:: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector{"Value1", "Value2"} ) for: { "Value1", "Value2" } Equals: { "Value1", "Value2" } +EnumToString.tests.cpp:: passed: parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector{"Value1", "Value2", "Value3"} ) for: { "Value1", "Value2", "Value3" } Equals: { "Value1", "Value2", "Value3" } +EnumToString.tests.cpp:: passed: parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector{"Value1", "Value2", "Value3"} ) for: { "Value1", "Value2", "Value3" } Equals: { "Value1", "Value2", "Value3" } Tricky.tests.cpp:: passed: p == 0 for: 0 == 0 Message.tests.cpp:: passed: true with 1 message: 'this MAY be seen IF info is printed for passing assertions' Message.tests.cpp:: failed: false with 2 messages: 'this SHOULD be seen' and 'this SHOULD also be seen' @@ -1379,6 +1398,9 @@ String.tests.cpp:: passed: s == "didn|'t" for: "didn|'t" == "didn|' Misc.tests.cpp:: failed: false with 1 message: '3' Message.tests.cpp:: failed: false with 2 messages: 'hi' and 'i := 7' Tag.tests.cpp:: passed: testcase.tags, Catch::VectorContains(std::string("magic-tag")) && Catch::VectorContains(std::string(".")) for: { ".", "magic-tag" } ( Contains: "magic-tag" and Contains: "." ) +String.tests.cpp:: passed: splitString("", ',' ), Equals(std::vector() ) for: { } Equals: { } +String.tests.cpp:: passed: splitString("abc", ',' ), Equals(std::vector{"abc"} ) for: { "abc" } Equals: { "abc" } +String.tests.cpp:: passed: splitString("abc,def", ',' ), Equals(std::vector{"abc", "def"} ) for: { "abc", "def" } Equals: { "abc", "def" } Message.tests.cpp:: failed: false with 4 messages: 'Count 1 to 3...' and '1' and '2' and '3' Message.tests.cpp:: failed: false with 4 messages: 'Count 4 to 6...' and '4' and '5' and '6' ToStringGeneral.tests.cpp:: passed: Catch::Detail::stringify( emptyMap ) == "{ }" for: "{ }" == "{ }" diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index d69a96c7..8f62dad4 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -1265,6 +1265,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 257 | 191 passed | 62 failed | 4 failed as expected -assertions: 1419 | 1276 passed | 122 failed | 21 failed as expected +test cases: 261 | 195 passed | 62 failed | 4 failed as expected +assertions: 1437 | 1294 passed | 122 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 06290486..77341bed 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -2225,6 +2225,29 @@ with expansion: "this string contains 'abc' as a substring" ends with: "this" (case insensitive) +------------------------------------------------------------------------------- +EnumInfo +------------------------------------------------------------------------------- +EnumToString.tests.cpp: +............................................................................... + +EnumToString.tests.cpp:: PASSED: + CHECK( enumInfo.lookup(0) == "Value1" ) +with expansion: + "Value1" == "Value1" + +EnumToString.tests.cpp:: PASSED: + CHECK( enumInfo.lookup(1) == "Value2" ) +with expansion: + "Value2" == "Value2" + +EnumToString.tests.cpp:: PASSED: + CHECK( enumInfo.lookup(3) == "{** unexpected value for EnumName: 3**}" ) +with expansion: + "{** unexpected value for EnumName: 3**}" + == + "{** unexpected value for EnumName: 3**}" + ------------------------------------------------------------------------------- Epsilon only applies to Approx's value ------------------------------------------------------------------------------- @@ -6684,6 +6707,39 @@ Matchers.tests.cpp:: PASSED: with expansion: { 'a', 'b' } not UnorderedEquals: { 'c', 'b' } +------------------------------------------------------------------------------- +STRINGIFY_ENUM +------------------------------------------------------------------------------- +EnumToString.tests.cpp: +............................................................................... + +EnumToString.tests.cpp:: PASSED: + REQUIRE( stringify( EnumClass3::Value1 ) == "Value1" ) +with expansion: + "Value1" == "Value1" + +EnumToString.tests.cpp:: PASSED: + REQUIRE( stringify( EnumClass3::Value2 ) == "Value2" ) +with expansion: + "Value2" == "Value2" + +EnumToString.tests.cpp:: PASSED: + REQUIRE( stringify( EnumClass3::Value3 ) == "Value3" ) +with expansion: + "Value3" == "Value3" + +EnumToString.tests.cpp:: PASSED: + REQUIRE( stringify( EnumClass3::Value4 ) == "{** unexpected value for EnumClass3: 3**}" ) +with expansion: + "{** unexpected value for EnumClass3: 3**}" + == + "{** unexpected value for EnumClass3: 3**}" + +EnumToString.tests.cpp:: PASSED: + REQUIRE( stringify( ec3 ) == "Value2" ) +with expansion: + "Value2" == "Value2" + ------------------------------------------------------------------------------- SUCCEED counts as a test pass ------------------------------------------------------------------------------- @@ -10025,6 +10081,62 @@ with expansion: == "{ { 42, "Arthur" }, { "Ford", 24 } }" +------------------------------------------------------------------------------- +parseEnums + No enums +------------------------------------------------------------------------------- +EnumToString.tests.cpp: +............................................................................... + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "" ), Equals( std::vector{} ) ) +with expansion: + { } Equals: { } + +------------------------------------------------------------------------------- +parseEnums + One enum value +------------------------------------------------------------------------------- +EnumToString.tests.cpp: +............................................................................... + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector{"Value1"} ) ) +with expansion: + { "Value1" } Equals: { "Value1" } + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "Value1" ), Equals( std::vector{"Value1"} ) ) +with expansion: + { "Value1" } Equals: { "Value1" } + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "EnumName::Value1" ), Equals(std::vector{"Value1"} ) ) +with expansion: + { "Value1" } Equals: { "Value1" } + +------------------------------------------------------------------------------- +parseEnums + Multiple enum values +------------------------------------------------------------------------------- +EnumToString.tests.cpp: +............................................................................... + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector{"Value1", "Value2"} ) ) +with expansion: + { "Value1", "Value2" } Equals: { "Value1", "Value2" } + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector{"Value1", "Value2", "Value3"} ) ) +with expansion: + { "Value1", "Value2", "Value3" } Equals: { "Value1", "Value2", "Value3" } + +EnumToString.tests.cpp:: PASSED: + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector{"Value1", "Value2", "Value3"} ) ) +with expansion: + { "Value1", "Value2", "Value3" } Equals: { "Value1", "Value2", "Value3" } + ------------------------------------------------------------------------------- pointer to class ------------------------------------------------------------------------------- @@ -10272,6 +10384,27 @@ Tag.tests.cpp:: PASSED: with expansion: { ".", "magic-tag" } ( Contains: "magic-tag" and Contains: "." ) +------------------------------------------------------------------------------- +splitString +------------------------------------------------------------------------------- +String.tests.cpp: +............................................................................... + +String.tests.cpp:: PASSED: + CHECK_THAT( splitString("", ',' ), Equals(std::vector() ) ) +with expansion: + { } Equals: { } + +String.tests.cpp:: PASSED: + CHECK_THAT( splitString("abc", ',' ), Equals(std::vector{"abc"} ) ) +with expansion: + { "abc" } Equals: { "abc" } + +String.tests.cpp:: PASSED: + CHECK_THAT( splitString("abc,def", ',' ), Equals(std::vector{"abc", "def"} ) ) +with expansion: + { "abc", "def" } Equals: { "abc", "def" } + ------------------------------------------------------------------------------- stacks unscoped info in loops ------------------------------------------------------------------------------- @@ -11113,6 +11246,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 257 | 176 passed | 77 failed | 4 failed as expected -assertions: 1435 | 1276 passed | 138 failed | 21 failed as expected +test cases: 261 | 180 passed | 77 failed | 4 failed as expected +assertions: 1453 | 1294 passed | 138 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index e985133b..90ee5838 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -5,7 +5,7 @@ loose text artifact - + @@ -234,6 +234,7 @@ Matchers.tests.cpp: Matchers.tests.cpp: + @@ -615,6 +616,7 @@ Matchers.tests.cpp: + @@ -945,6 +947,9 @@ Message.tests.cpp: + + + @@ -983,6 +988,7 @@ Message.tests.cpp: + Count 1 to 3... diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index 70845085..7fc8fc05 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -2670,6 +2670,35 @@ Nor would this + + + + enumInfo.lookup(0) == "Value1" + + + "Value1" == "Value1" + + + + + enumInfo.lookup(1) == "Value2" + + + "Value2" == "Value2" + + + + + enumInfo.lookup(3) == "{** unexpected value for EnumName: 3**}" + + + "{** unexpected value for EnumName: 3**}" +== +"{** unexpected value for EnumName: 3**}" + + + + @@ -8374,6 +8403,51 @@ Nor would this + + + + stringify( EnumClass3::Value1 ) == "Value1" + + + "Value1" == "Value1" + + + + + stringify( EnumClass3::Value2 ) == "Value2" + + + "Value2" == "Value2" + + + + + stringify( EnumClass3::Value3 ) == "Value3" + + + "Value3" == "Value3" + + + + + stringify( EnumClass3::Value4 ) == "{** unexpected value for EnumClass3: 3**}" + + + "{** unexpected value for EnumClass3: 3**}" +== +"{** unexpected value for EnumClass3: 3**}" + + + + + stringify( ec3 ) == "Value2" + + + "Value2" == "Value2" + + + + @@ -12197,6 +12271,74 @@ loose text artifact + +
+ + + parseEnums( "" ), Equals( std::vector<std::string>{} ) + + + { } Equals: { } + + + +
+
+ + + parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector<std::string>{"Value1"} ) + + + { "Value1" } Equals: { "Value1" } + + + + + parseEnums( "Value1" ), Equals( std::vector<std::string>{"Value1"} ) + + + { "Value1" } Equals: { "Value1" } + + + + + parseEnums( "EnumName::Value1" ), Equals(std::vector<std::string>{"Value1"} ) + + + { "Value1" } Equals: { "Value1" } + + + +
+
+ + + parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector<std::string>{"Value1", "Value2"} ) + + + { "Value1", "Value2" } Equals: { "Value1", "Value2" } + + + + + parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector<std::string>{"Value1", "Value2", "Value3"} ) + + + { "Value1", "Value2", "Value3" } Equals: { "Value1", "Value2", "Value3" } + + + + + parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector<std::string>{"Value1", "Value2", "Value3"} ) + + + { "Value1", "Value2", "Value3" } Equals: { "Value1", "Value2", "Value3" } + + + +
+ +
@@ -12494,6 +12636,33 @@ loose text artifact + + + + splitString("", ',' ), Equals(std::vector<std::string>() ) + + + { } Equals: { } + + + + + splitString("abc", ',' ), Equals(std::vector<std::string>{"abc"} ) + + + { "abc" } Equals: { "abc" } + + + + + splitString("abc,def", ',' ), Equals(std::vector<std::string>{"abc", "def"} ) + + + { "abc", "def" } Equals: { "abc", "def" } + + + + Count 1 to 3... @@ -13388,7 +13557,7 @@ loose text artifact - + - + diff --git a/projects/SelfTest/IntrospectiveTests/String.tests.cpp b/projects/SelfTest/IntrospectiveTests/String.tests.cpp index ae21bb3c..007774a9 100644 --- a/projects/SelfTest/IntrospectiveTests/String.tests.cpp +++ b/projects/SelfTest/IntrospectiveTests/String.tests.cpp @@ -202,3 +202,14 @@ TEST_CASE( "replaceInPlace", "[Strings][StringManip]" ) { CHECK( s == "didn|'t" ); } } + +TEST_CASE( "splitString", "[Strings]" ) { + using namespace Catch::Matchers; + using Catch::splitString; + + CHECK_THAT( splitString("", ',' ), Equals(std::vector() ) ); + CHECK_THAT( splitString("abc", ',' ), Equals(std::vector{"abc"} ) ); + CHECK_THAT( splitString("abc,def", ',' ), Equals(std::vector{"abc", "def"} ) ); +} + + diff --git a/projects/SelfTest/UsageTests/EnumToString.tests.cpp b/projects/SelfTest/UsageTests/EnumToString.tests.cpp index 0b188a82..bdd36315 100644 --- a/projects/SelfTest/UsageTests/EnumToString.tests.cpp +++ b/projects/SelfTest/UsageTests/EnumToString.tests.cpp @@ -64,3 +64,57 @@ TEST_CASE( "toString(enum class w/operator<<)", "[toString][enum][enumClass]" ) EnumClass2 e3 = static_cast(10); CHECK( ::Catch::Detail::stringify(e3) == "Unknown enum value 10" ); } + +enum class EnumClass3 { Value1, Value2, Value3, Value4 }; + +STRINGIFY_ENUM( EnumClass3, EnumClass3::Value1, EnumClass3::Value2, EnumClass3::Value3 ) + + +TEST_CASE( "STRINGIFY_ENUM" ) { + using Catch::Detail::stringify; + REQUIRE( stringify( EnumClass3::Value1 ) == "Value1" ); + REQUIRE( stringify( EnumClass3::Value2 ) == "Value2" ); + REQUIRE( stringify( EnumClass3::Value3 ) == "Value3" ); + REQUIRE( stringify( EnumClass3::Value4 ) == "{** unexpected value for EnumClass3: 3**}" ); + + EnumClass3 ec3 = EnumClass3 ::Value2; + REQUIRE( stringify( ec3 ) == "Value2" ); +} + +#include "internal/catch_interfaces_enum_values_registry.h" + +TEST_CASE( "EnumInfo" ) { + + auto& hub = Catch::getMutableRegistryHub(); + auto& reg = hub.getMutableEnumValuesRegistry(); + auto const& enumInfo = reg.registerEnum( "EnumName", "EnumName::Value1, EnumName::Value2", {0, 1} ); + + CHECK( enumInfo.lookup(0) == "Value1" ); + CHECK( enumInfo.lookup(1) == "Value2" ); + CHECK( enumInfo.lookup(3) == "{** unexpected value for EnumName: 3**}" ); +} + +#include "internal/catch_enum_values_registry.h" + +TEST_CASE( "parseEnums", "[Strings][enums]" ) { + using namespace Catch::Matchers; + using Catch::Detail::parseEnums; + + SECTION( "No enums" ) + CHECK_THAT( parseEnums( "" ), Equals( std::vector{} ) ); + + SECTION( "One enum value" ) { + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector{"Value1"} ) ); + CHECK_THAT( parseEnums( "Value1" ), Equals( std::vector{"Value1"} ) ); + CHECK_THAT( parseEnums( "EnumName::Value1" ), Equals(std::vector{"Value1"} ) ); + } + + SECTION( "Multiple enum values" ) { + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), + Equals( std::vector{"Value1", "Value2"} ) ); + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), + Equals( std::vector{"Value1", "Value2", "Value3"} ) ); + CHECK_THAT( parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), + Equals( std::vector{"Value1", "Value2", "Value3"} ) ); + } +}