/* * catch_capture.hpp * Catch * * Created by Phil on 18/10/2010. * Copyright 2010 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_CAPTURE_HPP_INCLUDED #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED #include "catch_resultinfo.hpp" #include "catch_debugger.hpp" #include #include namespace Catch { namespace Detail { // The following code, contributed by Sam Partington, allows us to choose an implementation // of toString() depending on whether a << overload is available struct NonStreamable { // allow construction from anything... template NonStreamable(Anything) {} }; // a local operator<< which may be called if there isn't a better one elsewhere... inline NonStreamable operator << ( std::ostream&, const NonStreamable& ns ) { return ns; } template struct IsStreamable { static NoType Deduce( const NonStreamable& ); static YesType Deduce( std::ostream& ); enum { value = sizeof( Deduce( Synth() << Synth() ) ) == sizeof( YesType ) }; }; // << is available, so use it with ostringstream to make the string template struct StringMaker { static std::string apply( const T& value ) { std::ostringstream oss; oss << value; return oss.str(); } }; // << not available - use a default string template struct StringMaker { static std::string apply( const T& ) { return "{?}"; } }; }// end namespace Detail template std::string toString( const T& value ) { return Detail::StringMaker::value>::apply( value ); } class TestFailureException { }; class DummyExceptionType_DontUse { }; class MutableResultInfo : public ResultInfo { public: MutableResultInfo() {} MutableResultInfo( const std::string& expr, bool isNot, const std::string& filename, std::size_t line, const std::string& macroName ) : ResultInfo( ( isNot ? "!" : "" ) + expr, ResultWas::Unknown, isNot, filename, line, macroName ) { } void setResultType( ResultWas::OfType result ) { // Flip bool results if isNot is set if( m_isNot && result == ResultWas::Ok ) m_result = ResultWas::ExpressionFailed; else if( m_isNot && result == ResultWas::ExpressionFailed ) m_result = ResultWas::Ok; else m_result = result; } void setMessage( const std::string& message ) { m_message = message; } template MutableResultInfo& operator ||( const RhsT& ) { m_expressionIncomplete = true; return *this; } private: friend class ResultBuilder; void setLhs( const std::string& lhs ) { m_lhs = lhs; } MutableResultInfo& setRhs( const std::string& op, const std::string& rhs ) { m_op = op; m_rhs = rhs; return *this; } }; class ResultBuilder { public: ResultBuilder( const char* expr, bool isNot, const std::string& filename, std::size_t line, const std::string& macroName ) : m_result( expr, isNot, filename, line, macroName ) {} template ResultBuilder& operator->*(const T & operand) { m_result.setLhs( toString( operand ) ); return *this; } template MutableResultInfo& operator == ( const RhsT& rhs ) { return m_result.setRhs( "==", toString( rhs ) ); } template MutableResultInfo& operator != ( const RhsT& rhs ) { return m_result.setRhs( "!=", toString( rhs ) ); } template MutableResultInfo& operator < ( const RhsT& rhs ) { return m_result.setRhs( "<", toString( rhs ) ); } template MutableResultInfo& operator > ( const RhsT& rhs ) { return m_result.setRhs( ">", toString( rhs ) ); } template MutableResultInfo& operator <= ( const RhsT& rhs ) { return m_result.setRhs( "<=", toString( rhs ) ); } template MutableResultInfo& operator >= ( const RhsT& rhs ) { return m_result.setRhs( ">=", toString( rhs ) ); } operator MutableResultInfo&() { return m_result; } private: MutableResultInfo m_result; }; class TestCaseInfo; class ScopedInfo; struct IResultListener { virtual ~IResultListener(){} virtual void testEnded( const ResultInfo& result ) = 0; virtual bool sectionStarted( const std::string& name, const std::string& description, std::size_t& successes, std::size_t& failures ) = 0; virtual void sectionEnded( const std::string& name, std::size_t successes, std::size_t failures ) = 0; virtual void pushScopedInfo( ScopedInfo* scopedInfo ) = 0; virtual void popScopedInfo( ScopedInfo* scopedInfo ) = 0; virtual bool shouldDebugBreak() const = 0; }; struct ResultAction { enum Value { None, Failed = 1, // Failure - but no debug break if Debug bit not set DebugFailed = 3 // Indicates that the debugger should break, if possible }; }; class ResultsCapture { private: ResultsCapture() : m_listener( 0 ) { } static ResultsCapture& instance() { static ResultsCapture instance; return instance; } public: static IResultListener* setListener( IResultListener* listener ) { IResultListener* prevListener = instance().m_listener; instance().m_listener = listener; return prevListener; } static void acceptExpression( const MutableResultInfo& resultInfo ) { instance().currentResult = resultInfo; } static ResultAction::Value acceptResult( bool result ) { return acceptResult( result ? ResultWas::Ok : ResultWas::ExpressionFailed ); } static ResultAction::Value acceptResult( ResultWas::OfType result ) { MutableResultInfo& currentResult = instance().currentResult; currentResult.setResultType( result ); if( instance().m_listener ) { instance().m_listener->testEnded( currentResult ); } bool ok = currentResult.ok(); instance().currentResult = MutableResultInfo(); if( ok ) return ResultAction::None; else if( instance().m_listener->shouldDebugBreak() ) return ResultAction::DebugFailed; else return ResultAction::Failed; } static void acceptMessage( const std::string& msg ) { instance().currentResult.setMessage( msg ); } static bool acceptSectionStart( const std::string& name, const std::string& description, std::size_t& successes, std::size_t& failures ) { return instance().m_listener->sectionStarted( name, description, successes, failures ); } static void acceptSectionEnd( const std::string& name, std::size_t successes, std::size_t failures ) { instance().m_listener->sectionEnded( name, successes, failures ); } static void pushScopedInfo( ScopedInfo* scopedInfo ) { instance().m_listener->pushScopedInfo( scopedInfo ); } static void popScopedInfo( ScopedInfo* scopedInfo ) { instance().m_listener->popScopedInfo( scopedInfo ); } private: MutableResultInfo currentResult; IResultListener* m_listener; }; class ScopedInfo { public: ScopedInfo() { ResultsCapture::pushScopedInfo( this ); } ~ScopedInfo() { ResultsCapture::popScopedInfo( this ); } ScopedInfo& operator << ( const char* str ) { m_oss << str; return *this; } std::string getInfo() const { return m_oss.str(); } private: std::ostringstream m_oss; }; // !TBD Need to clean this all up #define CATCH_absTol 1e-10 #define CATCH_relTol 1e-10 inline double catch_max( double x, double y ) { return x > y ? x : y; } class Approx { public: // !TBD more generic Approx( double d ) : m_d( d ) { } template friend bool operator == ( const T& lhs, const Approx& rhs ) { // !TBD Use proper tolerance // From: http://realtimecollisiondetection.net/blog/?p=89 // see also: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm return fabs( lhs - rhs.m_d ) <= catch_max( CATCH_absTol, CATCH_relTol * catch_max( fabs(lhs), fabs(rhs.m_d) ) ); } template friend bool operator != ( const T& lhs, const Approx& rhs ) { return ! operator==( lhs, rhs ); } double m_d; }; template<> inline std::string toString( const Approx& value ) { std::ostringstream oss; oss << "Approx( " << value.m_d << ")"; return oss.str(); } // This is just here to avoid compiler warnings with macro constants inline bool isTrue( bool value ) { return value; } } // end namespace Catch #define INTERNAL_CATCH_ACCEPT_RESULT( result, stopOnFailure ) \ if( Catch::ResultAction::Value action = Catch::ResultsCapture::acceptResult( result ) ) \ { \ if( action == Catch::ResultAction::DebugFailed ) DebugBreak(); \ if( Catch::isTrue( stopOnFailure ) ) throw Catch::TestFailureException(); \ } #define INTERNAL_CATCH_TEST( expr, isNot, stopOnFailure, macroName ) \ { \ Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, isNot, __FILE__, __LINE__, macroName )->*expr ); \ INTERNAL_CATCH_ACCEPT_RESULT( expr, stopOnFailure ) \ } #define INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, false, __FILE__, __LINE__, macroName ) ); \ try \ { \ expr; \ INTERNAL_CATCH_ACCEPT_RESULT( nothrow, stopOnFailure ) \ } \ catch( exceptionType ) \ { \ INTERNAL_CATCH_ACCEPT_RESULT( !(nothrow), stopOnFailure ) \ } #define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ catch( ... ) \ { \ INTERNAL_CATCH_ACCEPT_RESULT( false, stopOnFailure ) \ } #define INTERNAL_CATCH_MSG( reason, resultType, stopOnFailure, macroName ) \ { \ std::ostringstream INTERNAL_CATCH_UNIQUE_NAME( strm ); \ INTERNAL_CATCH_UNIQUE_NAME( strm ) << reason; \ Catch::ResultsCapture::acceptExpression( Catch::MutableResultInfo( "", false, __FILE__, __LINE__, macroName ) ); \ Catch::ResultsCapture::acceptMessage( INTERNAL_CATCH_UNIQUE_NAME( strm ).str() ); \ INTERNAL_CATCH_ACCEPT_RESULT( resultType, stopOnFailure ) \ } #define INTERNAL_CATCH_SCOPED_INFO( log ) Catch::ScopedInfo INTERNAL_CATCH_UNIQUE_NAME( info ); INTERNAL_CATCH_UNIQUE_NAME( info ) << log #endif // TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED