mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +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:
		| @@ -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"); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský