mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 13:26:10 +01:00
Add stringification support to std::exception and deriving classes
This support is based on overriden `std::exception::what` method, so if an exception does not do so meaningfully, the message is still pointless. This is only used as a fallback, both `StringMaker` specialization and `operator<<` overload have priority..
This commit is contained in:
parent
c323658483
commit
6c5c4c43a0
@ -62,7 +62,9 @@ namespace Catch {
|
||||
std::string convertUnknownEnumToString( E e );
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<!std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) {
|
||||
typename std::enable_if<
|
||||
!std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
|
||||
std::string>::type convertUnstreamable( T const& value ) {
|
||||
#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
|
||||
(void)value;
|
||||
return Detail::unprintableString;
|
||||
@ -71,11 +73,20 @@ namespace Catch {
|
||||
#endif
|
||||
}
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) {
|
||||
return convertUnknownEnumToString( value );
|
||||
typename std::enable_if<
|
||||
!std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
|
||||
std::string>::type convertUnstreamable(T const& ex) {
|
||||
return ex.what();
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
std::is_enum<T>::value
|
||||
, std::string>::type convertUnstreamable( T const& value ) {
|
||||
return convertUnknownEnumToString( value );
|
||||
}
|
||||
|
||||
#if defined(_MANAGED)
|
||||
//! Convert a CLR string to a utf8 std::string
|
||||
template<typename T>
|
||||
|
@ -211,14 +211,21 @@ Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals("this
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals("this string contains 'ABC' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
|
||||
ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(WhatException{}) == "This exception has overriden what() method" for: "This exception has overriden what() method"
|
||||
==
|
||||
"This exception has overriden what() method"
|
||||
ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(OperatorException{}) == "OperatorException" for: "OperatorException" == "OperatorException"
|
||||
ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException" for: "StringMakerException"
|
||||
==
|
||||
"StringMakerException"
|
||||
Matchers.tests.cpp:<line number>: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1}
|
||||
Matchers.tests.cpp:<line number>: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1}
|
||||
Matchers.tests.cpp:<line number>: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1}
|
||||
Matchers.tests.cpp:<line number>: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1}
|
||||
Matchers.tests.cpp:<line number>: failed: throws(3), SpecialException, ExceptionMatcher{1} for: {?} special exception has value of 1
|
||||
Matchers.tests.cpp:<line number>: failed: throws(4), SpecialException, ExceptionMatcher{1} for: {?} special exception has value of 1
|
||||
Matchers.tests.cpp:<line number>: passed: throws(1), SpecialException, ExceptionMatcher{1} for: {?} special exception has value of 1
|
||||
Matchers.tests.cpp:<line number>: passed: throws(2), SpecialException, ExceptionMatcher{2} for: {?} special exception has value of 2
|
||||
Matchers.tests.cpp:<line number>: failed: throws(3), SpecialException, ExceptionMatcher{1} for: std::exception special exception has value of 1
|
||||
Matchers.tests.cpp:<line number>: failed: throws(4), SpecialException, ExceptionMatcher{1} for: std::exception special exception has value of 1
|
||||
Matchers.tests.cpp:<line number>: passed: throws(1), SpecialException, ExceptionMatcher{1} for: std::exception special exception has value of 1
|
||||
Matchers.tests.cpp:<line number>: passed: throws(2), SpecialException, ExceptionMatcher{2} for: std::exception special exception has value of 2
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive)
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected"
|
||||
|
@ -335,12 +335,12 @@ Matchers.tests.cpp:<line number>
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THROWS_MATCHES( throws(3), SpecialException, ExceptionMatcher{1} )
|
||||
with expansion:
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
REQUIRE_THROWS_MATCHES( throws(4), SpecialException, ExceptionMatcher{1} )
|
||||
with expansion:
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Expected exceptions that don't throw or unexpected exceptions fail the test
|
||||
@ -1084,6 +1084,6 @@ due to unexpected exception with message:
|
||||
Why would you throw a std::string?
|
||||
|
||||
===============================================================================
|
||||
test cases: 206 | 153 passed | 49 failed | 4 failed as expected
|
||||
assertions: 1061 | 933 passed | 107 failed | 21 failed as expected
|
||||
test cases: 207 | 154 passed | 49 failed | 4 failed as expected
|
||||
assertions: 1064 | 936 passed | 107 failed | 21 failed as expected
|
||||
|
||||
|
@ -1680,6 +1680,34 @@ with expansion:
|
||||
"this string contains 'abc' as a substring" equals: "something else" (case
|
||||
insensitive)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified
|
||||
-------------------------------------------------------------------------------
|
||||
ToStringGeneral.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
ToStringGeneral.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( ::Catch::Detail::stringify(WhatException{}) == "This exception has overriden what() method" )
|
||||
with expansion:
|
||||
"This exception has overriden what() method"
|
||||
==
|
||||
"This exception has overriden what() method"
|
||||
|
||||
ToStringGeneral.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( ::Catch::Detail::stringify(OperatorException{}) == "OperatorException" )
|
||||
with expansion:
|
||||
"OperatorException" == "OperatorException"
|
||||
|
||||
ToStringGeneral.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE( ::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException" )
|
||||
with expansion:
|
||||
"StringMakerException"
|
||||
==
|
||||
"StringMakerException"
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception matchers that fail
|
||||
No exception
|
||||
@ -1722,12 +1750,12 @@ Matchers.tests.cpp:<line number>
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
CHECK_THROWS_MATCHES( throws(3), SpecialException, ExceptionMatcher{1} )
|
||||
with expansion:
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
|
||||
Matchers.tests.cpp:<line number>: FAILED:
|
||||
REQUIRE_THROWS_MATCHES( throws(4), SpecialException, ExceptionMatcher{1} )
|
||||
with expansion:
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception matchers that succeed
|
||||
@ -1739,13 +1767,13 @@ Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
CHECK_THROWS_MATCHES( throws(1), SpecialException, ExceptionMatcher{1} )
|
||||
with expansion:
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
|
||||
Matchers.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
REQUIRE_THROWS_MATCHES( throws(2), SpecialException, ExceptionMatcher{2} )
|
||||
with expansion:
|
||||
{?} special exception has value of 2
|
||||
std::exception special exception has value of 2
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Exception messages can be tested for
|
||||
@ -8950,6 +8978,6 @@ Misc.tests.cpp:<line number>:
|
||||
PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 206 | 140 passed | 62 failed | 4 failed as expected
|
||||
assertions: 1075 | 933 passed | 121 failed | 21 failed as expected
|
||||
test cases: 207 | 141 passed | 62 failed | 4 failed as expected
|
||||
assertions: 1078 | 936 passed | 121 failed | 21 failed as expected
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuitesloose text artifact
|
||||
>
|
||||
<testsuite name="<exe-name>" errors="17" failures="105" tests="1076" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testsuite name="<exe-name>" errors="17" failures="105" tests="1079" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="#1027" time="{duration}"/>
|
||||
@ -218,6 +218,7 @@ Matchers.tests.cpp:<line number>
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified" time="{duration}"/>
|
||||
<testcase classname="<exe-name>.global" name="Exception matchers that fail/No exception" time="{duration}">
|
||||
<failure message="doesNotThrow(), SpecialException, ExceptionMatcher{1}" type="CHECK_THROWS_MATCHES">
|
||||
Matchers.tests.cpp:<line number>
|
||||
@ -237,10 +238,10 @@ Matchers.tests.cpp:<line number>
|
||||
</error>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Exception matchers that fail/Contents are wrong" time="{duration}">
|
||||
<failure message="{?} special exception has value of 1" type="CHECK_THROWS_MATCHES">
|
||||
<failure message="std::exception special exception has value of 1" type="CHECK_THROWS_MATCHES">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
<failure message="{?} special exception has value of 1" type="REQUIRE_THROWS_MATCHES">
|
||||
<failure message="std::exception special exception has value of 1" type="REQUIRE_THROWS_MATCHES">
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
|
@ -1901,6 +1901,37 @@
|
||||
</Expression>
|
||||
<OverallResult success="false"/>
|
||||
</TestCase>
|
||||
<TestCase name="Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified" tags="[exception][toString]" filename="projects/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
|
||||
<Original>
|
||||
::Catch::Detail::stringify(WhatException{}) == "This exception has overriden what() method"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"This exception has overriden what() method"
|
||||
==
|
||||
"This exception has overriden what() method"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
|
||||
<Original>
|
||||
::Catch::Detail::stringify(OperatorException{}) == "OperatorException"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"OperatorException" == "OperatorException"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
|
||||
<Original>
|
||||
::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException"
|
||||
</Original>
|
||||
<Expanded>
|
||||
"StringMakerException"
|
||||
==
|
||||
"StringMakerException"
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="Exception matchers that fail" tags="[!throws][.][.failing][exceptions][matchers]" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Section name="No exception" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
<Expression success="false" type="CHECK_THROWS_MATCHES" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@ -1952,7 +1983,7 @@
|
||||
throws(3), SpecialException, ExceptionMatcher{1}
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@ -1960,7 +1991,7 @@
|
||||
throws(4), SpecialException, ExceptionMatcher{1}
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResults successes="0" failures="2" expectedFailures="0"/>
|
||||
@ -1973,7 +2004,7 @@
|
||||
throws(1), SpecialException, ExceptionMatcher{1}
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} special exception has value of 1
|
||||
std::exception special exception has value of 1
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="projects/<exe-name>/UsageTests/Matchers.tests.cpp" >
|
||||
@ -1981,7 +2012,7 @@
|
||||
throws(2), SpecialException, ExceptionMatcher{2}
|
||||
</Original>
|
||||
<Expanded>
|
||||
{?} special exception has value of 2
|
||||
std::exception special exception has value of 2
|
||||
</Expanded>
|
||||
</Expression>
|
||||
<OverallResult success="true"/>
|
||||
@ -9892,7 +9923,7 @@ loose text artifact
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="933" failures="122" expectedFailures="21"/>
|
||||
<OverallResults successes="936" failures="122" expectedFailures="21"/>
|
||||
</Group>
|
||||
<OverallResults successes="933" failures="121" expectedFailures="21"/>
|
||||
<OverallResults successes="936" failures="121" expectedFailures="21"/>
|
||||
</Catch>
|
||||
|
@ -115,3 +115,46 @@ TEST_CASE("Static arrays are convertible to string", "[toString]") {
|
||||
REQUIRE(Catch::Detail::stringify(arr) == R"({ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } })");
|
||||
}
|
||||
}
|
||||
|
||||
struct WhatException : std::exception {
|
||||
char const* what() const noexcept override {
|
||||
return "This exception has overriden what() method";
|
||||
}
|
||||
~WhatException() override;
|
||||
};
|
||||
|
||||
struct OperatorException : std::exception {
|
||||
~OperatorException() override;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, OperatorException const&) {
|
||||
out << "OperatorException";
|
||||
return out;
|
||||
}
|
||||
|
||||
struct StringMakerException : std::exception {
|
||||
~StringMakerException() override;
|
||||
};
|
||||
|
||||
namespace Catch {
|
||||
template <>
|
||||
struct StringMaker<StringMakerException> {
|
||||
static std::string convert(StringMakerException const&) {
|
||||
return "StringMakerException";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Avoid -Wweak-tables
|
||||
WhatException::~WhatException() = default;
|
||||
OperatorException::~OperatorException() = default;
|
||||
StringMakerException::~StringMakerException() = default;
|
||||
|
||||
|
||||
|
||||
|
||||
TEST_CASE("Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified", "[toString][exception]") {
|
||||
REQUIRE(::Catch::Detail::stringify(WhatException{}) == "This exception has overriden what() method");
|
||||
REQUIRE(::Catch::Detail::stringify(OperatorException{}) == "OperatorException");
|
||||
REQUIRE(::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user