mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 12:17:11 +01:00 
			
		
		
		
	First cut of new AssertionHandler/ Decomposer
- integrated into INTERNAL_CATCH_TEST. Needs more work to fully replace existing stuff
This commit is contained in:
		| @@ -122,6 +122,8 @@ CheckFileList(EXTERNAL_HEADERS ${HEADER_DIR}/external) | ||||
| # Please keep these ordered alphabetically | ||||
| set(INTERNAL_HEADERS | ||||
|         ${HEADER_DIR}/internal/catch_approx.hpp | ||||
|         ${HEADER_DIR}/internal/catch_assertionhandler.h | ||||
|         ${HEADER_DIR}/internal/catch_assertioninfo.h | ||||
|         ${HEADER_DIR}/internal/catch_assertionresult.h | ||||
|         ${HEADER_DIR}/internal/catch_capture.hpp | ||||
|         ${HEADER_DIR}/internal/catch_clara.h | ||||
| @@ -132,6 +134,7 @@ set(INTERNAL_HEADERS | ||||
|         ${HEADER_DIR}/internal/catch_console_colour.hpp | ||||
|         ${HEADER_DIR}/internal/catch_context.h | ||||
|         ${HEADER_DIR}/internal/catch_debugger.h | ||||
|         ${HEADER_DIR}/internal/catch_decomposer.h | ||||
|         ${HEADER_DIR}/internal/catch_default_main.hpp | ||||
|         ${HEADER_DIR}/internal/catch_enforce.h | ||||
|         ${HEADER_DIR}/internal/catch_errno_guard.h | ||||
| @@ -198,6 +201,7 @@ set(INTERNAL_HEADERS | ||||
|         ) | ||||
| set(IMPL_SOURCES | ||||
|         ${HEADER_DIR}/internal/catch_approx.cpp | ||||
|         ${HEADER_DIR}/internal/catch_assertionhandler.cpp | ||||
|         ${HEADER_DIR}/internal/catch_assertionresult.cpp | ||||
|         ${HEADER_DIR}/internal/catch_benchmark.cpp | ||||
|         ${HEADER_DIR}/internal/catch_commandline.cpp | ||||
| @@ -206,6 +210,7 @@ set(IMPL_SOURCES | ||||
|         ${HEADER_DIR}/internal/catch_console_colour.cpp | ||||
|         ${HEADER_DIR}/internal/catch_context.cpp | ||||
|         ${HEADER_DIR}/internal/catch_debugger.cpp | ||||
|         ${HEADER_DIR}/internal/catch_decomposer.cpp | ||||
|         ${HEADER_DIR}/internal/catch_errno_guard.cpp | ||||
|         ${HEADER_DIR}/internal/catch_evaluate.cpp | ||||
|         ${HEADER_DIR}/internal/catch_exception_translator_registry.cpp | ||||
|   | ||||
							
								
								
									
										135
									
								
								include/internal/catch_assertionhandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								include/internal/catch_assertionhandler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| /* | ||||
|  *  Created by Phil on 8/8/2017. | ||||
|  *  Copyright 2017 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_assertionhandler.h" | ||||
| #include "catch_assertionresult.h" | ||||
| #include "catch_interfaces_capture.h" | ||||
| #include "catch_interfaces_runner.h" | ||||
| #include "catch_context.h" | ||||
| #include "catch_debugger.h" | ||||
| #include "catch_interfaces_registry_hub.h" | ||||
|  | ||||
| #include <iostream> // !TBD | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { | ||||
|         expr.streamReconstructedExpression( os ); | ||||
|         return os; | ||||
|     } | ||||
|  | ||||
|     LazyExpression::LazyExpression( bool isNegated ) | ||||
|     :   m_isNegated( isNegated ) | ||||
|     {} | ||||
|  | ||||
|     LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} | ||||
|  | ||||
|     LazyExpression::operator bool() const { | ||||
|         return m_transientExpression != nullptr; | ||||
|     } | ||||
|  | ||||
|     auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { | ||||
|         if( lazyExpr.m_isNegated ) | ||||
|             os << "!"; | ||||
|  | ||||
|         if( lazyExpr ) { | ||||
|             if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) | ||||
|                 os << "(" << *lazyExpr.m_transientExpression << ")"; | ||||
|             else | ||||
|                 os << *lazyExpr.m_transientExpression; | ||||
|         } | ||||
|         else { | ||||
|             os << "{** error - unchecked empty expression requested **}"; | ||||
|         } | ||||
|         return os; | ||||
|     } | ||||
|  | ||||
|     AssertionHandler::AssertionHandler | ||||
|         (   StringRef macroName, | ||||
|             SourceLineInfo const& lineInfo, | ||||
|             StringRef capturedExpression, | ||||
|             ResultDisposition::Flags resultDisposition ) | ||||
|     :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } | ||||
|     { | ||||
|         getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); | ||||
|     } | ||||
|  | ||||
|     void AssertionHandler::handle( ITransientExpression const& expr ) { | ||||
|  | ||||
|         bool negated = isFalseTest( m_assertionInfo.resultDisposition ); | ||||
|         bool result = expr.getResult() != negated; | ||||
|  | ||||
|         AssertionResultData data( result ? ResultWas::Ok : ResultWas::ExpressionFailed, LazyExpression( negated ) ); | ||||
|  | ||||
|         // Deprecated | ||||
| //        data.negated = negated; | ||||
| //        data.parenthesized = negated && expr.isBinaryExpression(); // !TBD: needed? | ||||
| //        data.decomposedExpression = nullptr; // !TBD | ||||
| //        data.reconstructedExpression = ""; // !TBD | ||||
|  | ||||
|  | ||||
|         getResultCapture().assertionRun(); | ||||
|  | ||||
|         AssertionResult assertionResult{ m_assertionInfo, data }; | ||||
|         assertionResult.m_resultData.lazyExpression.m_transientExpression = &expr; | ||||
|  | ||||
|         getResultCapture().assertionEnded( assertionResult ); | ||||
|  | ||||
|         if( !assertionResult.isOk() ) { | ||||
|             m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak(); | ||||
|             m_shouldThrow = | ||||
|                     getCurrentContext().getRunner()->aborting() || | ||||
|                     (m_assertionInfo.resultDisposition & ResultDisposition::Normal); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     auto AssertionHandler::shouldDebugBreak() const -> bool { | ||||
|         return m_shouldDebugBreak; | ||||
|     } | ||||
|     void AssertionHandler::reactWithDebugBreak() const { | ||||
|         if (m_shouldDebugBreak) { | ||||
|             /////////////////////////////////////////////////////////////////// | ||||
|             // To inspect the state during test, you need to go one level up the callstack | ||||
|             // To go back to the test and change execution, jump over the reactWithoutDebugBreak() call | ||||
|             /////////////////////////////////////////////////////////////////// | ||||
|             CATCH_BREAK_INTO_DEBUGGER(); | ||||
|         } | ||||
|         reactWithoutDebugBreak(); | ||||
|     } | ||||
|     void AssertionHandler::reactWithoutDebugBreak() const { | ||||
|         if( m_shouldThrow ) | ||||
|             throw Catch::TestFailureException(); | ||||
|     } | ||||
|  | ||||
|     void AssertionHandler::useActiveException( ResultDisposition::Flags resultDisposition ) { | ||||
|         m_assertionInfo.resultDisposition = resultDisposition; | ||||
|         useActiveException(); | ||||
|     } | ||||
|     void AssertionHandler::useActiveException() { | ||||
|         bool negated = isFalseTest( m_assertionInfo.resultDisposition ); | ||||
|  | ||||
|         AssertionResultData data( ResultWas::ThrewException, LazyExpression( negated ) ); | ||||
|         data.message = Catch::translateActiveException(); | ||||
|  | ||||
|         //data.decomposedExpression = &expr; // for lazy reconstruction | ||||
|  | ||||
|         AssertionResult result( m_assertionInfo, data ); | ||||
|  | ||||
|         getResultCapture().assertionEnded( result ); | ||||
|  | ||||
|         // !TBD: factor this out? handleResult()? | ||||
|         if( !result.isOk() ) { | ||||
|             if( getCurrentContext().getConfig()->shouldDebugBreak() ) | ||||
|                 m_shouldDebugBreak = true; | ||||
|             if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) | ||||
|                 m_shouldThrow = true; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } // namespace Catch | ||||
							
								
								
									
										61
									
								
								include/internal/catch_assertionhandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								include/internal/catch_assertionhandler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /* | ||||
|  *  Created by Phil on 8/8/2017. | ||||
|  *  Copyright 2017 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_ASSERTIONHANDLER_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED | ||||
|  | ||||
| #include "catch_decomposer.h" | ||||
| #include "catch_assertioninfo.h" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     struct TestFailureException{}; | ||||
|  | ||||
|     class LazyExpression { | ||||
|         friend class AssertionHandler; | ||||
|         friend struct AssertionStats; | ||||
|  | ||||
|         ITransientExpression const* m_transientExpression = nullptr; | ||||
|         bool m_isNegated; | ||||
|     public: | ||||
|         LazyExpression( bool isNegated ); | ||||
|         LazyExpression( LazyExpression const& other ); | ||||
|         LazyExpression& operator = ( LazyExpression const& ) = delete; | ||||
|  | ||||
|         explicit operator bool() const; | ||||
|  | ||||
|         friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; | ||||
|     }; | ||||
|  | ||||
|     class AssertionHandler { | ||||
|         AssertionInfo m_assertionInfo; | ||||
|         bool m_shouldDebugBreak = false; | ||||
|         bool m_shouldThrow = false; | ||||
|     public: | ||||
|         AssertionHandler | ||||
|             (   StringRef macroName, | ||||
|                 SourceLineInfo const& lineInfo, | ||||
|                 StringRef capturedExpression, | ||||
|                 ResultDisposition::Flags resultDisposition ); | ||||
|  | ||||
|         void handle( ITransientExpression const& expr ); | ||||
|  | ||||
|         template<typename T> | ||||
|         void handle( ExprLhs<T> const& expr ) { | ||||
|             handle( expr.makeUnaryExpr() ); | ||||
|         } | ||||
|  | ||||
|         auto shouldDebugBreak() const -> bool; | ||||
|         void reactWithDebugBreak() const; | ||||
|         void reactWithoutDebugBreak() const; | ||||
|         void useActiveException( ResultDisposition::Flags resultDisposition ); | ||||
|         void useActiveException(); | ||||
|     }; | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED | ||||
							
								
								
									
										29
									
								
								include/internal/catch_assertioninfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								include/internal/catch_assertioninfo.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
|  *  Created by Phil on 8/8/2017. | ||||
|  *  Copyright 2017 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_ASSERTIONINFO_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_ASSERTIONINFO_H_INCLUDED | ||||
|  | ||||
| #include "catch_result_type.h" | ||||
| #include "catch_common.h" | ||||
| #include "catch_stringref.h" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     struct AssertionInfo | ||||
|     { | ||||
|         StringRef macroName; | ||||
|         SourceLineInfo lineInfo; | ||||
|         StringRef capturedExpression; | ||||
|         ResultDisposition::Flags resultDisposition; | ||||
|  | ||||
|         AssertionInfo() = delete; | ||||
|     }; | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_ASSERTIONINFO_H_INCLUDED | ||||
| @@ -24,17 +24,30 @@ namespace Catch { | ||||
|             resultType = ResultWas::Ok; | ||||
|     } | ||||
|  | ||||
|     std::string const& AssertionResultData::reconstructExpression() const { | ||||
|         if( decomposedExpression != nullptr ) { | ||||
|             decomposedExpression->reconstructExpression( reconstructedExpression ); | ||||
|             if( parenthesized ) { | ||||
|                 reconstructedExpression.insert( 0, 1, '(' ); | ||||
|                 reconstructedExpression.append( 1, ')' ); | ||||
|     std::string AssertionResultData::reconstructExpression() const { | ||||
|  | ||||
|         if( reconstructedExpression.empty() ) { | ||||
|  | ||||
|             // Try new LazyExpression first... | ||||
|             if( lazyExpression ) { | ||||
|                 // !TBD Use stringstream for now, but rework above to pass stream in | ||||
|                 std::ostringstream oss; | ||||
|                 oss << lazyExpression; | ||||
|                 reconstructedExpression = oss.str(); | ||||
|             } | ||||
|             if( negated ) { | ||||
|                 reconstructedExpression.insert( 0, 1, '!' ); | ||||
|  | ||||
|             // ... if not, fall back to decomposedExpression | ||||
|             else if( decomposedExpression != nullptr ) { | ||||
|                 decomposedExpression->reconstructExpression( reconstructedExpression ); | ||||
|                 if( parenthesized ) { | ||||
|                     reconstructedExpression.insert( 0, 1, '(' ); | ||||
|                     reconstructedExpression.append( 1, ')' ); | ||||
|                 } | ||||
|                 if( negated ) { | ||||
|                     reconstructedExpression.insert( 0, 1, '!' ); | ||||
|                 } | ||||
|                 decomposedExpression = nullptr; | ||||
|             } | ||||
|             decomposedExpression = nullptr; | ||||
|         } | ||||
|         return reconstructedExpression; | ||||
|     } | ||||
| @@ -85,7 +98,10 @@ namespace Catch { | ||||
|     } | ||||
|  | ||||
|     std::string AssertionResult::getExpandedExpression() const { | ||||
|         return m_resultData.reconstructExpression(); | ||||
|         std::string expr = m_resultData.reconstructExpression(); | ||||
|         return expr.empty() | ||||
|                 ? getExpression() | ||||
|                 : expr; | ||||
|     } | ||||
|  | ||||
|     std::string AssertionResult::getMessage() const { | ||||
|   | ||||
| @@ -9,9 +9,11 @@ | ||||
| #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED | ||||
|  | ||||
| #include <string> | ||||
| #include "catch_assertioninfo.h" | ||||
| #include "catch_result_type.h" | ||||
| #include "catch_common.h" | ||||
| #include "catch_stringref.h" | ||||
| #include "catch_assertionhandler.h" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
| @@ -37,28 +39,32 @@ namespace Catch { | ||||
|         template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); | ||||
|         template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); | ||||
|     }; | ||||
| } | ||||
|  | ||||
|     struct AssertionInfo | ||||
|     { | ||||
|         StringRef macroName; | ||||
|         SourceLineInfo lineInfo; | ||||
|         StringRef capturedExpression; | ||||
|         ResultDisposition::Flags resultDisposition; | ||||
|  | ||||
|         AssertionInfo() = delete; | ||||
|     }; | ||||
| namespace Catch { | ||||
|  | ||||
|     struct AssertionResultData | ||||
|     { | ||||
|         void negate( bool parenthesize ); | ||||
|         std::string const& reconstructExpression() const; | ||||
|         AssertionResultData() = delete; | ||||
|  | ||||
|         // !TBD We won't need this constructor once the deprecated fields are removed | ||||
|         AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ) | ||||
|         :   resultType( _resultType ), | ||||
|             lazyExpression( _lazyExpression ) | ||||
|         {} | ||||
|  | ||||
|         mutable DecomposedExpression const* decomposedExpression = nullptr; | ||||
|         mutable std::string reconstructedExpression; | ||||
|         std::string message; | ||||
|         ResultWas::OfType resultType = ResultWas::Unknown; | ||||
|         std::string message; | ||||
|  | ||||
|         LazyExpression lazyExpression; | ||||
|  | ||||
|         // deprecated: | ||||
|         bool negated = false; | ||||
|         bool parenthesized = false; | ||||
|         void negate( bool parenthesize ); | ||||
|         std::string reconstructExpression() const; | ||||
|         mutable DecomposedExpression const* decomposedExpression = nullptr; | ||||
|         mutable std::string reconstructedExpression; | ||||
|     }; | ||||
|  | ||||
|     class AssertionResult { | ||||
| @@ -81,7 +87,7 @@ namespace Catch { | ||||
|         void discardDecomposedExpression() const; | ||||
|         void expandDecomposedExpression() const; | ||||
|  | ||||
|     protected: | ||||
|     //protected: | ||||
|         AssertionInfo m_info; | ||||
|         AssertionResultData m_resultData; | ||||
|     }; | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED | ||||
|  | ||||
| #include "catch_assertionhandler.h" | ||||
| #include "catch_result_builder.h" | ||||
| #include "catch_message.h" | ||||
| #include "catch_interfaces_capture.h" | ||||
| @@ -43,7 +44,10 @@ | ||||
| // source code rather than in Catch library code | ||||
| #define INTERNAL_CATCH_REACT( resultBuilder ) \ | ||||
|     if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ | ||||
|     resultBuilder.react();  | ||||
|     resultBuilder.react(); | ||||
| #define INTERNAL_CATCH_REACT2( handler ) \ | ||||
|     if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ | ||||
|     handler.reactWithDebugBreak(); | ||||
|  | ||||
| #define INTERNAL_CATCH_TRY try | ||||
| #define INTERNAL_CATCH_CATCH( capturer, disposition ) catch(...) { capturer.useActiveException( disposition ); } | ||||
| @@ -53,13 +57,13 @@ | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ | ||||
|     do { \ | ||||
|         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__, resultDisposition ); \ | ||||
|         Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__, resultDisposition ); \ | ||||
|         INTERNAL_CATCH_TRY { \ | ||||
|             CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ | ||||
|             ( __catchResult <= __VA_ARGS__ ).endExpression(); \ | ||||
|             catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ | ||||
|             CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ | ||||
|         } INTERNAL_CATCH_CATCH( __catchResult, resultDisposition ) \ | ||||
|         INTERNAL_CATCH_REACT( __catchResult ) \ | ||||
|         } INTERNAL_CATCH_CATCH( catchAssertionHandler, resultDisposition ) \ | ||||
|         INTERNAL_CATCH_REACT2( catchAssertionHandler ) \ | ||||
|     } while( Catch::isTrue( false && static_cast<bool>( !!(__VA_ARGS__) ) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look | ||||
|     // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. | ||||
|  | ||||
|   | ||||
							
								
								
									
										23
									
								
								include/internal/catch_decomposer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								include/internal/catch_decomposer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| /* | ||||
|  *  Created by Phil Nash on 8/8/2017. | ||||
|  *  Copyright 2017 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_decomposer.h" | ||||
| #include "catch_config.hpp" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     void formatReconstructedExpression( std::ostream &os, std::string const& lhs, std::string const& op, std::string const& rhs ) { | ||||
|  | ||||
|         if( lhs.size() + rhs.size() < 40 && | ||||
|                 lhs.find('\n') == std::string::npos && | ||||
|                 rhs.find('\n') == std::string::npos ) | ||||
|             os << lhs << " " << op << " " << rhs; | ||||
|         else | ||||
|             os << lhs << "\n" << op << "\n" << rhs; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										149
									
								
								include/internal/catch_decomposer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								include/internal/catch_decomposer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| /* | ||||
|  *  Created by Phil Nash on 8/8/2017. | ||||
|  *  Copyright 2017 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_DECOMPOSER_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED | ||||
|  | ||||
| #include "catch_tostring.h" | ||||
| #include "catch_stringref.h" | ||||
|  | ||||
| #include <ostream> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     struct ITransientExpression { | ||||
|         virtual auto isBinaryExpression() const -> bool = 0; | ||||
|         virtual auto getResult() const -> bool = 0; | ||||
|         virtual void streamReconstructedExpression( std::ostream &os ) const = 0; | ||||
|     }; | ||||
|  | ||||
|     void formatReconstructedExpression( std::ostream &os, std::string const& lhs, std::string const& op, std::string const& rhs ); | ||||
|  | ||||
|     template<typename LhsT, typename RhsT> | ||||
|     class BinaryExpr  : public ITransientExpression { | ||||
|         bool m_result; | ||||
|         LhsT m_lhs; | ||||
|         std::string m_op; | ||||
|         RhsT m_rhs; | ||||
|  | ||||
|         auto isBinaryExpression() const -> bool override { return true; } | ||||
|         auto getResult() const -> bool override { return m_result; } | ||||
|  | ||||
|         void streamReconstructedExpression( std::ostream &os ) const override { | ||||
|             formatReconstructedExpression | ||||
|                     ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); | ||||
|         } | ||||
|  | ||||
|     public: | ||||
|         BinaryExpr( bool comparisionResult, LhsT lhs, StringRef op, RhsT rhs ) | ||||
|         :   m_result( comparisionResult ), | ||||
|             m_lhs( lhs ), | ||||
|             m_op( op.c_str() ), | ||||
|             m_rhs( rhs ) | ||||
|         {} | ||||
|     }; | ||||
|  | ||||
|     template<typename LhsT> | ||||
|     class UnaryExpr : public ITransientExpression { | ||||
|         LhsT m_lhs; | ||||
|  | ||||
|         auto isBinaryExpression() const -> bool override { return false; } | ||||
|         auto getResult() const -> bool override { return m_lhs ? true : false; } | ||||
|  | ||||
|         void streamReconstructedExpression( std::ostream &os ) const override { | ||||
|             os << Catch::Detail::stringify( m_lhs ); | ||||
|         } | ||||
|  | ||||
|     public: | ||||
|         UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {} | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) | ||||
|     template<typename LhsT, typename RhsT> | ||||
|     auto compareEqual( LhsT lhs, RhsT&& rhs ) -> bool { return lhs == rhs; }; | ||||
|     template<typename T> | ||||
|     auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }; | ||||
|     template<typename T> | ||||
|     auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }; | ||||
|  | ||||
|     template<typename LhsT, typename RhsT> | ||||
|     auto compareNotEqual( LhsT lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; | ||||
|     template<typename T> | ||||
|     auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }; | ||||
|     template<typename T> | ||||
|     auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }; | ||||
|  | ||||
|  | ||||
|     template<typename LhsT> | ||||
|     class ExprLhs { | ||||
|         LhsT m_lhs; | ||||
|     public: | ||||
|         ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} | ||||
|  | ||||
|         template<typename RhsT> | ||||
|         auto operator == ( RhsT&& rhs ) -> BinaryExpr<LhsT, RhsT&> const { | ||||
|             return BinaryExpr<LhsT, RhsT&>( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs ); | ||||
|         } | ||||
|         auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { | ||||
|             return BinaryExpr<LhsT, bool>( m_lhs == rhs, m_lhs, "==", rhs ); | ||||
|         } | ||||
|  | ||||
|         template<typename RhsT> | ||||
|         auto operator != ( RhsT&& rhs ) -> BinaryExpr<LhsT, RhsT&> const { | ||||
|             return BinaryExpr<LhsT, RhsT&>( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs ); | ||||
|         } | ||||
|         auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { | ||||
|             return BinaryExpr<LhsT, bool>( m_lhs != rhs, m_lhs, "!=", rhs ); | ||||
|         } | ||||
|  | ||||
|         template<typename RhsT> | ||||
|         auto operator > ( RhsT&& rhs ) -> BinaryExpr<LhsT, RhsT&> const { | ||||
|             return BinaryExpr<LhsT, RhsT&>( m_lhs > rhs, m_lhs, ">", rhs ); | ||||
|         } | ||||
|         template<typename RhsT> | ||||
|         auto operator < ( RhsT&& rhs ) -> BinaryExpr<LhsT, RhsT&> const { | ||||
|             return BinaryExpr<LhsT, RhsT&>( m_lhs < rhs, m_lhs, "<", rhs ); | ||||
|         } | ||||
|         template<typename RhsT> | ||||
|         auto operator >= ( RhsT&& rhs ) -> BinaryExpr<LhsT, RhsT&> const { | ||||
|             return BinaryExpr<LhsT, RhsT&>( m_lhs >= rhs, m_lhs, ">=", rhs ); | ||||
|         } | ||||
|         template<typename RhsT> | ||||
|         auto operator <= ( RhsT&& rhs ) -> BinaryExpr<LhsT, RhsT&> const { | ||||
|             return BinaryExpr<LhsT, RhsT&>( m_lhs <= rhs, m_lhs, "<=", rhs ); | ||||
|         } | ||||
|  | ||||
|         auto makeUnaryExpr() const -> UnaryExpr<LhsT> { | ||||
|             return UnaryExpr<LhsT>( m_lhs ); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     void handleExpression( ITransientExpression const& expr ); | ||||
|  | ||||
|     template<typename T> | ||||
|     void handleExpression( ExprLhs<T> const& expr ) { | ||||
|         handleExpression( expr.makeUnaryExpr() ); | ||||
|     } | ||||
|  | ||||
|     struct Decomposer { | ||||
|         template<typename T> | ||||
|         auto operator <= ( T& lhs ) -> ExprLhs<T&> { | ||||
|             return ExprLhs<T&>( lhs ); | ||||
|         } | ||||
|         template<typename T> | ||||
|         auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { | ||||
|             return ExprLhs<T const&>( lhs ); | ||||
|         } | ||||
|         auto operator <=( bool value ) -> ExprLhs<bool> { | ||||
|             return ExprLhs<bool>( value ); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_DECOMPOSER_H_INCLUDED | ||||
| @@ -37,6 +37,8 @@ namespace Catch { | ||||
|         infoMessages( _infoMessages ), | ||||
|         totals( _totals ) | ||||
|     { | ||||
|         assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; | ||||
|  | ||||
|         if( assertionResult.hasMessage() ) { | ||||
|             // Copy message into messages list. | ||||
|             // !TBD This should have been done earlier, somewhere | ||||
|   | ||||
| @@ -13,7 +13,6 @@ | ||||
| #include "catch_interfaces_capture.h" | ||||
| #include "catch_interfaces_registry_hub.h" | ||||
| #include "catch_matchers_string.h" | ||||
| #include "catch_wildcard_pattern.hpp" | ||||
| #include "catch_debugger.h" | ||||
|  | ||||
| #include <cassert> | ||||
| @@ -34,7 +33,8 @@ namespace Catch { | ||||
|                                     SourceLineInfo const& lineInfo, | ||||
|                                     StringRef capturedExpression, | ||||
|                                     ResultDisposition::Flags resultDisposition ) | ||||
|     :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition } | ||||
|     :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, | ||||
|         m_data( ResultWas::Unknown, LazyExpression( false ) ) | ||||
|     { | ||||
|         getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo ); | ||||
|     } | ||||
|   | ||||
| @@ -8,8 +8,10 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED | ||||
|  | ||||
| #include "catch_result_type.h" | ||||
| #include "catch_assertionresult.h" | ||||
| #include "catch_decomposer.h" | ||||
|  | ||||
| #include "catch_result_type.h" | ||||
| #include "catch_common.h" | ||||
| #include "catch_matchers.hpp" | ||||
|  | ||||
| @@ -17,8 +19,6 @@ | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     struct TestFailureException{}; | ||||
|  | ||||
|     template<typename T> class ExpressionLhs; | ||||
|  | ||||
|     struct CopyableStream { | ||||
|   | ||||
| @@ -192,7 +192,7 @@ namespace Catch { | ||||
|     void RunContext::handleFatalErrorCondition(std::string const & message) { | ||||
|         // Don't rebuild the result -- the stringification itself can cause more fatal errors | ||||
|         // Instead, fake a result data. | ||||
|         AssertionResultData tempResult; | ||||
|         AssertionResultData tempResult( ResultWas::Unknown, { false } ); | ||||
|         tempResult.resultType = ResultWas::FatalErrorCondition; | ||||
|         tempResult.message = message; | ||||
|         AssertionResult result(m_lastAssertionInfo, tempResult); | ||||
|   | ||||
| @@ -193,14 +193,14 @@ namespace Catch { | ||||
|  | ||||
|         bool assertionEnded(AssertionStats const& assertionStats) override { | ||||
|             assert(!m_sectionStack.empty()); | ||||
|             SectionNode& sectionNode = *m_sectionStack.back(); | ||||
|             sectionNode.assertions.push_back(assertionStats); | ||||
|             // AssertionResult holds a pointer to a temporary DecomposedExpression, | ||||
|             // which getExpandedExpression() calls to build the expression string. | ||||
|             // Our section stack copy of the assertionResult will likely outlive the | ||||
|             // temporary, so it must be expanded or discarded now to avoid calling | ||||
|             // a destroyed object later. | ||||
|             prepareExpandedExpression(sectionNode.assertions.back().assertionResult); | ||||
|             prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); | ||||
|             SectionNode& sectionNode = *m_sectionStack.back(); | ||||
|             sectionNode.assertions.push_back(assertionStats); | ||||
|             return true; | ||||
|         } | ||||
|         void sectionEnded(SectionStats const& sectionStats) override { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Phil Nash
					Phil Nash