From ce37f48ffad2401e6c23a54cae3f015c01fdc200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Wed, 1 Feb 2017 21:47:43 +0100 Subject: [PATCH] v1.7.0 build --- README.md | 4 +- docs/release-notes.md | 39 +- include/internal/catch_version.hpp | 2 +- single_include/catch.hpp | 1065 ++++++++++++++++++---------- 4 files changed, 740 insertions(+), 370 deletions(-) diff --git a/README.md b/README.md index d4afc81ccd..06edcc26bb 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ ![catch logo](catch-logo-small.png) -*v1.6.1* +*v1.7.0* Build status (on Travis CI) [![Build Status](https://travis-ci.org/philsquared/Catch.svg?branch=master)](https://travis-ci.org/philsquared/Catch) -The latest, single header, version can be downloaded directly using this link +The latest, single header, version can be downloaded directly using this link ## What's the Catch? diff --git a/docs/release-notes.md b/docs/release-notes.md index 5561dcb699..bec9910e7c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,4 +1,37 @@ -# 1.6.1 +# 1.7.0 + +### Features/ Changes: +* Catch now runs significantly faster for passing tests + * Microbenchmark focused on Catch's overhead went from ~3.4s to ~0.7s. + * Real world test using [JSON for Modern C++](https://github.com/nlohmann/json)'s test suite went from ~6m 25s to ~4m 14s. +* Catch can now run specific sections within test cases. + * For now the support is only basic (no wildcards or tags), for details see the [documentation](docs/command-line.md). +* Catch now supports SEH on Windows as well as signals on Linux. + * After receiving a signal, Catch reports failing assertion and then passes the signal onto the previous handler. +* Approx can be used to compare values against strong typedefs (available in C++11 mode only). + * Strong typedefs mean types that are explicitly convertible to double. +* CHECK macro no longer stops executing section if an exception happens. +* Certain characters (space, tab, etc) are now pretty printed. + * This means that a `char c = ' '; REQUIRE(c == '\t');` would be printed as `' ' == '\t'`, instead of ` == 9`. + +### Fixes: +* Text formatting no longer attempts to access out-of-bounds characters under certain conditions. +* THROW family of assertions no longer trigger `-Wunused-value` on expressions containing explicit cast. +* Breaking into debugger under OS X works again and no longer required `DEBUG` to be defined. +* Compilation no longer breaks under certain compiler if a lambda is used inside assertion macro. + +### Other: +* Catch's CMakeLists now defines install command. +* Catch's CMakeLists now generates projects with warnings enabled. + + + + + +# Older versions +Release notes were not maintained prior to v1.6.0, but you should be able to work them out from the Git history + +## 1.6.1 ### Features/ Changes: * Catch now supports breaking into debugger on Linux @@ -18,10 +51,6 @@ * This can be disabled if needed, see [documentation](docs/configuration.md) for details. - -# Older versions -Release notes were not maintained prior to v1.6.0, but you should be able to work them out from the Git history - ## 1.6.0 ### Cmake/ projects: diff --git a/include/internal/catch_version.hpp b/include/internal/catch_version.hpp index cb1265cf0f..279facf551 100644 --- a/include/internal/catch_version.hpp +++ b/include/internal/catch_version.hpp @@ -37,7 +37,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 6, 1, "", 0 ); + Version libraryVersion( 1, 7, 0, "", 0 ); } diff --git a/single_include/catch.hpp b/single_include/catch.hpp index 6bcdd04502..99c93f17f7 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.6.1 - * Generated: 2017-01-20 12:33:53.497767 + * Catch v1.7.0 + * Generated: 2017-02-01 21:32:13.239291 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -74,6 +74,8 @@ // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) +// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? +// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? @@ -167,6 +169,7 @@ #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER @@ -235,6 +238,9 @@ # if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) # define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE # endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif #endif // __cplusplus >= 201103L @@ -275,6 +281,9 @@ #if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_SHUFFLE #endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS @@ -377,7 +386,10 @@ namespace Catch { } 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 ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); @@ -397,8 +409,8 @@ namespace Catch { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; @@ -407,15 +419,16 @@ namespace Catch { bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; - std::string file; + char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool alwaysTrue( std::size_t = 0 ) { return true; } - inline bool alwaysFalse( std::size_t = 0 ) { return false; } + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); @@ -841,6 +854,27 @@ namespace Catch { namespace Catch { + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; + + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); + }; + struct AssertionInfo { AssertionInfo() {} @@ -857,11 +891,41 @@ namespace Catch { struct AssertionResultData { - AssertionResultData() : resultType( ResultWas::Unknown ) {} + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} + + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } - std::string reconstructedExpression; + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; + bool negated; + bool parenthesized; }; class AssertionResult { @@ -888,6 +952,8 @@ namespace Catch { std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; protected: AssertionInfo m_info; @@ -1074,7 +1140,7 @@ namespace Matchers { { return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" - : ""; + : std::string(); } CaseSensitive::Choice m_caseSensitivity; std::string m_str; @@ -1092,7 +1158,7 @@ namespace Matchers { return m_data.m_str == m_data.adjustString( expr );; } virtual std::string toString() const { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); } CasedString m_data; @@ -1109,7 +1175,7 @@ namespace Matchers { return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; } virtual std::string toString() const { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + return "contains: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); } CasedString m_data; @@ -1127,7 +1193,7 @@ namespace Matchers { return startsWith( m_data.adjustString( expr ), m_data.m_str ); } virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + return "starts with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); } CasedString m_data; @@ -1144,7 +1210,7 @@ namespace Matchers { return endsWith( m_data.adjustString( expr ), m_data.m_str ); } virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + return "ends with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); } CasedString m_data; @@ -1219,22 +1285,20 @@ namespace Catch { template class ExpressionLhs; - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); + oss.str(std::string()); oss << other.oss.str(); return *this; } std::ostringstream oss; }; - class ResultBuilder { + class ResultBuilder : public DecomposedExpression { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, @@ -1252,19 +1316,15 @@ namespace Catch { return *this; } - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); - ResultBuilder& setLhs( std::string const& lhs ); - ResultBuilder& setRhs( std::string const& rhs ); - ResultBuilder& setOp( std::string const& op ); - void endExpression(); + void endExpression( DecomposedExpression const& expr ); + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - std::string reconstructExpression() const; AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); @@ -1276,14 +1336,12 @@ namespace Catch { bool shouldDebugBreak() const; bool allowThrows() const; + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + private: AssertionInfo m_assertionInfo; AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; CopyableStream m_stream; bool m_shouldDebugBreak; @@ -1805,90 +1863,155 @@ std::string toString( T const& value ) { namespace Catch { -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in a ResultBuilder object -template -class ExpressionLhs { - ExpressionLhs& operator = ( ExpressionLhs const& ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif +template +class BinaryExpression; + +template +class MatchExpression; +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs( ExpressionLhs const& ) = default; - ExpressionLhs( ExpressionLhs && ) = default; -# endif + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} template - ResultBuilder& operator == ( RhsT const& rhs ) { + BinaryExpression + operator == ( RhsT const& rhs ) const { return captureExpression( rhs ); } template - ResultBuilder& operator != ( RhsT const& rhs ) { + BinaryExpression + operator != ( RhsT const& rhs ) const { return captureExpression( rhs ); } template - ResultBuilder& operator < ( RhsT const& rhs ) { + BinaryExpression + operator < ( RhsT const& rhs ) const { return captureExpression( rhs ); } template - ResultBuilder& operator > ( RhsT const& rhs ) { + BinaryExpression + operator > ( RhsT const& rhs ) const { return captureExpression( rhs ); } template - ResultBuilder& operator <= ( RhsT const& rhs ) { + BinaryExpression + operator <= ( RhsT const& rhs ) const { return captureExpression( rhs ); } template - ResultBuilder& operator >= ( RhsT const& rhs ) { + BinaryExpression + operator >= ( RhsT const& rhs ) const { return captureExpression( rhs ); } - ResultBuilder& operator == ( bool rhs ) { + BinaryExpression operator == ( bool rhs ) const { return captureExpression( rhs ); } - ResultBuilder& operator != ( bool rhs ) { + BinaryExpression operator != ( bool rhs ) const { return captureExpression( rhs ); } void endExpression() { - bool value = m_lhs ? true : false; + m_truthy = m_lhs ? true : false; m_rb - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression(); + .setResultType( m_truthy ) + .endExpression( *this ); } - // Only simple binary expressions are allowed on the LHS. - // If more complex compositions are required then place the sub expression in parentheses - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_truthy ); + } private: template - ResultBuilder& captureExpression( RhsT const& rhs ) { - return m_rb - .setResultType( Internal::compare( m_lhs, rhs ) ) - .setLhs( Catch::toString( m_lhs ) ) - .setRhs( Catch::toString( rhs ) ) - .setOp( Internal::OperatorTraits::getName() ); + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); } private: ResultBuilder& m_rb; T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; }; } // end namespace Catch @@ -1905,6 +2028,14 @@ namespace Catch { return ExpressionLhs( *this, value ); } + template + inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } + } // namespace Catch // #included from: catch_message.h @@ -2034,14 +2165,12 @@ namespace Catch{ // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html - #ifdef DEBUG - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_TRAP() \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ) - #else - #define CATCH_TRAP() _asm__("int $3\n" : : ) - #endif + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) + #else + #define CATCH_TRAP() __asm__("int $3\n" : : ) #endif #elif defined(CATCH_PLATFORM_LINUX) @@ -2098,10 +2227,11 @@ namespace Catch { ( __catchResult <= expr ).endExpression(); \ } \ catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse( sizeof(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr 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 &&. /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ @@ -2118,7 +2248,7 @@ namespace Catch { do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ @@ -2133,7 +2263,7 @@ namespace Catch { Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ @@ -2150,7 +2280,7 @@ namespace Catch { Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ @@ -2192,13 +2322,7 @@ namespace Catch { do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ try { \ - std::string matcherAsString = (matcher).toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( (matcher).match( arg ) ); \ - __catchResult.captureExpression(); \ + __catchResult.captureMatch( arg, matcher, #matcher ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ @@ -2651,6 +2775,10 @@ namespace Catch { #include #include +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + namespace Catch { namespace Detail { @@ -2679,6 +2807,53 @@ namespace Detail { return approx; } +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + auto lhs_v = double(lhs); + return fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs_v), fabs(rhs.m_value) ) ); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) + { + return double(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) + { + return lhs.m_value < double(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) + { + return double(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) + { + return lhs.m_value > double(rhs) || lhs == rhs; + } +#else friend bool operator == ( double lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); @@ -2715,6 +2890,7 @@ namespace Detail { { return lhs.m_value > rhs || lhs == rhs; } +#endif Approx& epsilon( double newEpsilon ) { m_epsilon = newEpsilon; @@ -2870,7 +3046,8 @@ namespace Catch { IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, - Throws = 1 << 4 + Throws = 1 << 4, + NonPortable = 1 << 5 }; TestCaseInfo( std::string const& _name, @@ -3182,11 +3359,11 @@ namespace Catch m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { - if( startsWith( m_pattern, "*" ) ) { + if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } - if( endsWith( m_pattern, "*" ) ) { + if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } @@ -3373,6 +3550,8 @@ namespace Catch { m_start = start; } void escape() { + if( m_mode == None ) + m_start = m_pos; m_mode = EscapedName; m_escapeChars.push_back( m_pos ); } @@ -3381,7 +3560,7 @@ namespace Catch { void addPattern() { std::string token = subString(); for( size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i] ) + token.substr( m_escapeChars[i]+1 ); + token = token.substr( 0, m_escapeChars[i]-i ) + token.substr( m_escapeChars[i]+1-i ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; @@ -3468,6 +3647,8 @@ namespace Catch { virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; } @@ -3594,6 +3775,7 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; class Config : public SharedImpl { @@ -3634,7 +3816,8 @@ namespace Catch { bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - std::vector getReporterNames() const { return m_data.reporterNames; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } int abortAfter() const { return m_data.abortAfter; } @@ -4724,13 +4907,14 @@ namespace Catch { config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) @@ -4740,7 +4924,7 @@ namespace Catch { else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { @@ -4786,10 +4970,10 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) { - if( !startsWith( line, "\"" ) ) - line = "\"" + line + "\""; - addTestOrTags( config, line + "," ); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); } } } @@ -4877,6 +5061,10 @@ namespace Catch { .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) @@ -4949,19 +5137,16 @@ namespace Tbc { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) + width( consoleWidth-1 ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos }; class Text { @@ -4969,62 +5154,76 @@ namespace Tbc { Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); + + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { - while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; } else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; } - } - } + lines.push_back( indent + std::string( it, itEnd ) + suffix ); - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; + } } typedef std::vector::const_iterator const_iterator; @@ -5421,9 +5620,9 @@ namespace Catch { } if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } @@ -5438,8 +5637,8 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, "#" ) ) - Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl; else Catch::cout() << testCaseInfo.name << std::endl; } @@ -5502,9 +5701,9 @@ namespace Catch { .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; + Catch::cout() << oss.str() << wrapper << '\n'; } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } @@ -5523,9 +5722,9 @@ namespace Catch { .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first - << ":" + << ':' << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; + << wrapper << '\n'; } Catch::cout() << std::endl; return factories.size(); @@ -5556,15 +5755,26 @@ namespace Catch { #include #include #include +#include namespace Catch { namespace TestCaseTracking { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -5580,7 +5790,7 @@ namespace TestCaseTracking { virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -5588,7 +5798,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -5650,30 +5860,32 @@ namespace TestCaseTracking { Failed }; class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; - std::string m_name; + NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: - TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : m_name( name ), + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -5692,8 +5904,8 @@ namespace TestCaseTracking { m_children.push_back( child ); } - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; @@ -5771,32 +5983,56 @@ namespace TestCaseTracking { }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { - section = new SectionTracker( name, ctx, ¤tTracker ); + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } - if( !ctx.completedCycle() && !section->isComplete() ) { + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } - section->open(); + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + std::copy( filters.begin(), filters.end(), std::back_inserter( m_filters ) ); } - return *section; + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + std::copy( filters.begin()+1, filters.end(), std::back_inserter( m_filters ) ); } }; @@ -5804,8 +6040,8 @@ namespace TestCaseTracking { int m_size; int m_index; public: - IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( name, ctx, parent ), + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} @@ -5813,17 +6049,17 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } @@ -5851,7 +6087,7 @@ namespace TestCaseTracking { }; inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; @@ -5871,25 +6107,77 @@ using TestCaseTracking::IndexTracker; namespace Catch { - // Report the error condition then exit the process - inline void fatal( std::string const& message, int exitCode ) { + // Report the error condition + inline void reportFatal( std::string const& message ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); - - if( Catch::alwaysTrue() ) // avoids "no return" warnings - exit( exitCode ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + struct FatalConditionHandler { - void reset() {} - }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + FatalConditionHandler():m_isSet(true), m_guaranteeSize(32 * 1024), m_exceptionHandlerHandle(CATCH_NULL) { + // Register as first handler in current chain + m_exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&m_guaranteeSize); + } + + void reset() { + if (m_isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(m_exceptionHandlerHandle); + SetThreadStackGuarantee(&m_guaranteeSize); + m_exceptionHandlerHandle = CATCH_NULL; + m_isSet = false; + } + } + + ~FatalConditionHandler() { + reset(); + } + private: + bool m_isSet; + ULONG m_guaranteeSize; + PVOID m_exceptionHandlerHandle; + }; } // namespace Catch @@ -5899,7 +6187,10 @@ namespace Catch { namespace Catch { - struct SignalDefs { int id; const char* name; }; + struct SignalDefs { + int id; + const char* name; + }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, @@ -5908,35 +6199,66 @@ namespace Catch { { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + }; struct FatalConditionHandler { + static bool isSet; + static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[SIGSTKSZ]; + static void handleSignal( int sig ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - if( sig == signalDefs[i].id ) - fatal( signalDefs[i].name, -sig ); - fatal( "", -sig ); + std::string name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); } - FatalConditionHandler() : m_isSet( true ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, handleSignal ); + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } } + ~FatalConditionHandler() { reset(); } - void reset() { - if( m_isSet ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, SIG_DFL ); - m_isSet = false; + static void reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); + } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + isSet = false; } } - - bool m_isSet; }; + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + } // namespace Catch #endif // not Windows @@ -6015,10 +6337,11 @@ namespace Catch { m_activeTestCase = &testCase; do { - m_trackerContext.startRun(); + ITracker& rootTracker = m_trackerContext.startRun(); + dynamic_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); @@ -6063,7 +6386,7 @@ namespace Catch { m_messages.clear(); // Reset working state - m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } @@ -6072,10 +6395,7 @@ namespace Catch { Counts& assertions ) { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); @@ -6134,7 +6454,7 @@ namespace Catch { virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name - : ""; + : std::string(); } virtual const AssertionResult* getLastResult() const { @@ -6164,11 +6484,11 @@ namespace Catch { deltaTotals.testCases.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, - "", - "", + std::string(), + std::string(), false ) ); m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); + testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } @@ -6187,7 +6507,7 @@ namespace Catch { Counts prevAssertions = m_totals.assertions; double duration = 0; try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal ); seedRng( *m_config ); @@ -6578,7 +6898,7 @@ namespace Catch { ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); @@ -6610,7 +6930,7 @@ namespace Catch { virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { + if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); @@ -6659,7 +6979,7 @@ namespace Catch { inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) + if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); @@ -6949,7 +7269,7 @@ namespace Catch { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; + oss << "Unable to open file: '" << filename << '\''; throw std::domain_error( oss.str() ); } } @@ -7410,7 +7730,7 @@ namespace Catch { std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; + return '!' + m_info.capturedExpression; else return m_info.capturedExpression; } @@ -7426,7 +7746,7 @@ namespace Catch { } std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; + return m_resultData.reconstructExpression(); } std::string AssertionResult::getMessage() const { @@ -7440,6 +7760,14 @@ namespace Catch { return m_info.macroName; } + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } + } // end namespace Catch // #included from: catch_test_case_info.hpp @@ -7448,7 +7776,7 @@ namespace Catch { namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || + if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; @@ -7458,6 +7786,8 @@ namespace Catch { return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; else return TestCaseInfo::None; } @@ -7532,7 +7862,7 @@ namespace Catch { std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; + oss << '[' << *it << ']'; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); @@ -7648,18 +7978,18 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." + os << version.majorVersion << '.' + << version.minorVersion << '.' << version.patchNumber; if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; + os << '-' << version.branchName + << '.' << version.buildNumber; } return os; } - Version libraryVersion( 1, 6, 1, "", 0 ); + Version libraryVersion( 1, 7, 0, "", 0 ); } @@ -7878,17 +8208,28 @@ namespace Catch { // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +#include + namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } + bool contains( std::string const& s, char infix ) { + return s.find(infix) != std::string::npos; + } char toLowerCh(char c) { return static_cast( ::tolower( c ) ); } @@ -7905,7 +8246,7 @@ namespace Catch { std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -7928,29 +8269,25 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; + os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) - os << "s"; + os << 's'; return os; } - SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} - SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} bool SourceLineInfo::empty() const { - return file.empty(); + return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } void seedRng( IConfig const& config ) { @@ -7963,16 +8300,16 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + os << info.file << ':' << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } @@ -8193,7 +8530,7 @@ std::string toString( std::string const& value ) { } } } - return "\"" + s + "\""; + return '"' + s + '"'; } std::string toString( std::wstring const& value ) { @@ -8214,19 +8551,19 @@ std::string toString( char* const value ) { std::string toString( const wchar_t* const value ) { - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { - return Catch::toString( static_cast( value ) ); + return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8234,7 +8571,7 @@ std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8262,7 +8599,7 @@ std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { - return fpToString( value, 5 ) + "f"; + return fpToString( value, 5 ) + 'f'; } std::string toString( bool value ) { @@ -8270,9 +8607,19 @@ std::string toString( bool value ) { } std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } std::string toString( signed char value ) { @@ -8288,14 +8635,14 @@ std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif @@ -8352,22 +8699,10 @@ namespace Catch { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } - ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ResultBuilder& ResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - void ResultBuilder::endExpression() { - m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); - captureExpression(); + void ResultBuilder::endExpression( DecomposedExpression const& expr ) { + AssertionResult result = build( expr ); + handleResult( result ); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { @@ -8380,6 +8715,7 @@ namespace Catch { setResultType( resultType ); captureExpression(); } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) captureExpectedException( Matchers::Impl::Generic::AllOf() ); @@ -8389,7 +8725,7 @@ namespace Catch { void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { - assert( m_exprComponents.testFalse == false ); + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; data.reconstructedExpression = m_assertionInfo.capturedExpression; @@ -8407,6 +8743,7 @@ namespace Catch { AssertionResult result = build(); handleResult( result ); } + void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); @@ -8418,6 +8755,7 @@ namespace Catch { m_shouldThrow = true; } } + void ResultBuilder::react() { if( m_shouldThrow ) throw Catch::TestFailureException(); @@ -8428,43 +8766,32 @@ namespace Catch { AssertionResult ResultBuilder::build() const { - assert( m_data.resultType != ResultWas::Unknown ); + return build( *this ); + } + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const + { + assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; - // Flip bool results if testFalse is set - if( m_exprComponents.testFalse ) { - if( data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + data.negate( expr.isBinaryExpression() ); } data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if( m_exprComponents.testFalse ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } + data.decomposedExpression = &expr; // for lazy reconstruction return AssertionResult( m_assertionInfo, data ); } - std::string ResultBuilder::reconstructExpression() const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs; - else if( m_exprComponents.op == "matches" ) - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if( m_exprComponents.op != "!" ) { - if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos ) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; - } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = m_assertionInfo.capturedExpression; } } // end namespace Catch @@ -8525,7 +8852,7 @@ namespace Catch { void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; throw std::domain_error( oss.str().c_str() ); @@ -8533,7 +8860,7 @@ namespace Catch { if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" + << "\tFirst seen at " << find(alias)->lineInfo << '\n' << "\tRedefined at " << lineInfo; throw std::domain_error( oss.str().c_str() ); } @@ -8801,12 +9128,12 @@ namespace Catch { struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { return node->stats.sectionInfo.lineInfo == m_other.lineInfo; } private: - void operator=( BySectionInfo const& ); + void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; @@ -8862,6 +9189,12 @@ namespace Catch { 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 ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { @@ -8896,6 +9229,13 @@ namespace Catch { virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + Ptr m_config; std::ostream& stream; std::vector m_assertions; @@ -9001,7 +9341,7 @@ namespace Catch { return new T( config ); } virtual std::string getDescription() const { - return ""; + return std::string(); } }; @@ -9149,7 +9489,7 @@ namespace Catch { XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); - stream() << m_indent << "<" << name; + stream() << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; @@ -9178,12 +9518,12 @@ namespace Catch { XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) - stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; + stream() << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + stream() << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } @@ -9215,7 +9555,7 @@ namespace Catch { XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << "\n"; + stream() << '\n'; return *this; } @@ -9240,7 +9580,7 @@ namespace Catch { void newlineIfNecessary() { if( m_needsNewline ) { - stream() << "\n"; + stream() << '\n'; m_needsNewline = false; } } @@ -9349,7 +9689,7 @@ namespace Catch { if( assertionResult.hasExpression() ) { m_xml.startElement( "Expression" ) .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) + .writeAttribute( "type", assertionResult.getTestMacroName() ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ); @@ -9584,7 +9924,7 @@ namespace Catch { SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) - name = rootName + "/" + name; + name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || @@ -9662,14 +10002,14 @@ namespace Catch { std::ostringstream oss; if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; + oss << result.getMessage() << '\n'; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) - oss << it->message << "\n"; + oss << it->message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); @@ -9704,7 +10044,7 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { @@ -9746,12 +10086,12 @@ namespace Catch { } if( m_headerPrinted ) { if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl; m_headerPrinted = false; } else { if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl; } StreamingReporterBase::sectionEnded( _sectionStats ); } @@ -9765,7 +10105,7 @@ namespace Catch { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } @@ -9857,13 +10197,13 @@ namespace Catch { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) - stream << "\n"; + stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { - stream << "\n"; + stream << '\n'; } printMessage(); } @@ -9880,25 +10220,25 @@ namespace Catch { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); - stream << "\n"; + stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; + stream << messageLabel << ':' << '\n'; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; } } void printSourceInfo() const { @@ -9930,7 +10270,7 @@ namespace Catch { } } void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; + stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion << " host application.\n" @@ -9964,19 +10304,19 @@ namespace Catch { SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; + stream << lineInfo << '\n'; } - stream << getLineOfChars<'.'>() << "\n" << std::endl; + stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; + stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); @@ -9993,7 +10333,7 @@ namespace Catch { i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; + .setInitialIndent( indent ) ) << '\n'; } struct SummaryColumn { @@ -10008,9 +10348,9 @@ namespace Catch { std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) - *it = " " + *it; + *it = ' ' + *it; while( it->size() > row.size() ) - row = " " + row; + row = ' ' + row; } rows.push_back( row ); return *this; @@ -10030,8 +10370,8 @@ namespace Catch { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; } else { @@ -10066,10 +10406,10 @@ namespace Catch { else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) - << value << " " << it->label; + << value << ' ' << it->label; } } - stream << "\n"; + stream << '\n'; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { @@ -10105,10 +10445,10 @@ namespace Catch { else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } - stream << "\n"; + stream << '\n'; } void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; } private: @@ -10143,7 +10483,7 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) { @@ -10170,7 +10510,7 @@ namespace Catch { virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -10270,26 +10610,26 @@ namespace Catch { void printSourceInfo() const { Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; + stream << result.getSourceInfo() << ':'; } void printResultType( Colour::Code colour, std::string passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); - stream << " " << passOrFail; + stream << ' ' << passOrFail; } - stream << ":"; + stream << ':'; } } void printIssue( std::string issue ) const { - stream << " " << issue; + stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { - stream << ";"; + stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; @@ -10300,7 +10640,7 @@ namespace Catch { void printOriginalExpression() const { if( result.hasExpression() ) { - stream << " " << result.getExpression(); + stream << ' ' << result.getExpression(); } } @@ -10316,7 +10656,7 @@ namespace Catch { void printMessage() { if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; ++itMessage; } } @@ -10331,13 +10671,13 @@ namespace Catch { { Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; + stream << " with " << pluralise( N, "message" ) << ':'; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; @@ -10363,7 +10703,7 @@ namespace Catch { // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { @@ -10374,12 +10714,12 @@ namespace Catch { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; + bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; + pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << @@ -10391,14 +10731,14 @@ namespace Catch { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; @@ -10484,7 +10824,8 @@ namespace Catch { // Standard C/C++ main entry point int main (int argc, char * argv[]) { - return Catch::Session().run( argc, argv ); + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ @@ -10502,7 +10843,7 @@ int main (int argc, char * const argv[]) { [pool drain]; #endif - return result; + return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__