diff --git a/README.md b/README.md
index 501ce6e3..b6c70d52 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@

-*v1.7.2*
+*v1.8.0*
Build status (on Travis CI) [](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 e1bd19d2..6922468f 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -1,3 +1,40 @@
+# 1.8.0
+
+### New features/ minor changes
+
+* Matchers have new, simpler (and documented) interface.
+ * Catch provides string and vector matchers.
+ * For details see [Matchers documentation](docs/matchers.md).
+* Changed console reporter test duration reporting format (#322)
+ * Old format: `Some simple comparisons between doubles completed in 0.000123s`
+ * New format: `xxx.123s: Some simple comparisons between doubles` _(There will always be exactly 3 decimal places)_
+* Added opt-in leak detection under MSVC + Windows (#439)
+ * Enable it by compiling Catch's main with `CATCH_CONFIG_WINDOWS_CRTDBG`
+* Introduced new compile-time flag, `CATCH_CONFIG_FAST_COMPILE`, trading features for compilation speed.
+ * Moves debug breaks out of tests and into implementation, speeding up compilation time by ~XX%
+ * _More changes are coming_
+* Added [TAP (Test Anything Protocol)](https://testanything.org/) and [Automake](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html#Log-files-generation-and-test-results-recording) reporters.
+ * These are not present in the default single-include header and need to be downloaded from GitHub separately.
+ * For details see [documentation about integrating with build systems](docs/build-systems.md).
+* XML reporter now reports filename as part of the `Section` and `TestCase` tags.
+* `Approx` now supports an optional margin of absolute error
+ * It has also received [new documentation]().
+
+### Fixes
+* Silenced C4312 ("conversion from int to 'ClassName *") warnings in the evaluate layer.
+* Fixed C4512 ("assignment operator could not be generated") warnings under VS2013.
+* Cygwin compatibility fixes
+ * Signal handling is no longer compiled by default.
+ * Usage of `gettimeofday` inside Catch should no longer cause compilation errors.
+* Improved `-Wparentheses` supression for gcc (#674)
+ * When compiled with gcc 4.8 or newer, the supression is localized to assertions only
+ * Otherwise it is supressed for the whole TU
+* Fixed test spec parser issue (with escapes in multiple names)
+
+### Other
+* Various documentation fixes and improvements
+
+
# 1.7.2
### Fixes and minor improvements
@@ -31,9 +68,6 @@ Other:
* Fixed possible infinite recursion in Windows SEH.
* Fixed possible compilation error caused by Catch's operator overloads being ambiguous in regards to user-defined templated operators.
-# 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.7.0
### Features/ Changes:
@@ -102,6 +136,9 @@ Release notes were not maintained prior to v1.6.0, but you should be able to wor
* Tweaks and changes to scripts - particularly for Approval test - to make them more portable
+# 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
+
---
[Home](Readme.md)
diff --git a/include/internal/catch_version.hpp b/include/internal/catch_version.hpp
index 1041e303..3c3593fa 100644
--- a/include/internal/catch_version.hpp
+++ b/include/internal/catch_version.hpp
@@ -37,7 +37,7 @@ namespace Catch {
return os;
}
- Version libraryVersion( 1, 7, 2, "", 0 );
+ Version libraryVersion( 1, 8, 0, "", 0 );
}
diff --git a/single_include/catch.hpp b/single_include/catch.hpp
index 2a09fd19..abba8388 100644
--- a/single_include/catch.hpp
+++ b/single_include/catch.hpp
@@ -1,6 +1,6 @@
/*
- * Catch v1.7.2
- * Generated: 2017-02-13 15:57:33.350226
+ * Catch v1.8.0
+ * Generated: 2017-02-28 14:16:45.289179
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -40,6 +40,12 @@
#elif defined __GNUC__
# pragma GCC diagnostic ignored "-Wvariadic-macros"
# pragma GCC diagnostic ignored "-Wunused-variable"
+
+ // For newer version we can use __Pragma to disable the warnings locally
+# if __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 7
+# pragma GCC diagnostic ignored "-Wparentheses"
+# endif
+
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpadded"
#endif
@@ -82,6 +88,7 @@
// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
// ****************
// Note to maintainers: if new toggles are added please document them
// in configuration.md, too
@@ -117,11 +124,25 @@
# endif
# if defined(CATCH_CPP11_OR_GREATER)
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
# endif
#endif // __clang__
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# endif
+
+#endif // __CYGWIN__
+
////////////////////////////////////////////////////////////////////////////////
// Borland
#ifdef __BORLANDC__
@@ -144,12 +165,20 @@
// GCC
#ifdef __GNUC__
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
+# define CATCH_GCC_HAS_NEW_PRAGMA
+# endif
+
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
# endif
-# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER)
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_GCC_HAS_NEW_PRAGMA)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "GCC diagnostic push" ) \
+ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "GCC diagnostic pop" )
# endif
// - otherwise more recent versions define __cplusplus >= 201103L
@@ -290,9 +319,14 @@
#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
# define CATCH_CONFIG_WINDOWS_SEH
#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
#endif
// noexcept support:
@@ -871,6 +905,9 @@ namespace Catch {
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& );
+
+ private:
+ DecomposedExpression& operator = (DecomposedExpression const&);
};
struct AssertionInfo
@@ -967,313 +1004,153 @@ namespace Catch {
namespace Matchers {
namespace Impl {
- namespace Generic {
- template class AllOf;
- template class AnyOf;
- template class Not;
- }
+ template struct MatchAllOf;
+ template struct MatchAnyOf;
+ template struct MatchNotOf;
- template
- struct Matcher : SharedImpl
- {
- typedef ExpressionT ExpressionType;
-
- virtual ~Matcher() {}
- virtual Ptr clone() const = 0;
- virtual bool match( ExpressionT const& expr ) const = 0;
- virtual std::string toString() const = 0;
-
- Generic::AllOf operator && ( Matcher const& other ) const;
- Generic::AnyOf operator || ( Matcher const& other ) const;
- Generic::Not operator ! () const;
- };
-
- template
- struct MatcherImpl : Matcher {
-
- virtual Ptr > clone() const {
- return Ptr >( new DerivedT( static_cast( *this ) ) );
- }
- };
-
- namespace Generic {
- template
- class Not : public MatcherImpl, ExpressionT> {
+ class MatcherUntypedBase {
public:
- explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {}
- Not( Not const& other ) : m_matcher( other.m_matcher ) {}
-
- virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE {
- return !m_matcher->match( expr );
+ std::string toString() const {
+ if( m_cachedToString.empty() )
+ m_cachedToString = describe();
+ return m_cachedToString;
}
- virtual std::string toString() const CATCH_OVERRIDE {
- return "not " + m_matcher->toString();
- }
+ protected:
+ virtual std::string describe() const = 0;
+ mutable std::string m_cachedToString;
private:
- Ptr< Matcher > m_matcher;
+ MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
};
- template
- class AllOf : public MatcherImpl, ExpressionT> {
- public:
+ template
+ struct MatcherBase : MatcherUntypedBase {
- AllOf() {}
- AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+ virtual bool match( ObjectT const& arg ) const = 0;
- AllOf& add( Matcher const& matcher ) {
- m_matchers.push_back( matcher.clone() );
- return *this;
- }
- virtual bool match( ExpressionT const& expr ) const
- {
- for( std::size_t i = 0; i < m_matchers.size(); ++i )
- if( !m_matchers[i]->match( expr ) )
+ MatchAllOf operator && ( MatcherBase const& other ) const;
+ MatchAnyOf operator || ( MatcherBase const& other ) const;
+ MatchNotOf operator ! () const;
+ };
+
+ template
+ struct MatchAllOf : MatcherBase {
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (!m_matchers[i]->match(arg))
return false;
+ }
return true;
}
- virtual std::string toString() const {
- std::ostringstream oss;
- oss << "( ";
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 )
- oss << " and ";
- oss << m_matchers[i]->toString();
+ description += " and ";
+ description += m_matchers[i]->toString();
}
- oss << " )";
- return oss.str();
+ description += " )";
+ return description;
}
- AllOf operator && ( Matcher const& other ) const {
- AllOf allOfExpr( *this );
- allOfExpr.add( other );
- return allOfExpr;
- }
-
- private:
- std::vector > > m_matchers;
- };
-
- template
- class AnyOf : public MatcherImpl, ExpressionT> {
- public:
-
- AnyOf() {}
- AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
- AnyOf& add( Matcher const& matcher ) {
- m_matchers.push_back( matcher.clone() );
+ MatchAllOf& operator && ( MatcherBase const& other ) {
+ m_matchers.push_back( &other );
return *this;
}
- virtual bool match( ExpressionT const& expr ) const
- {
- for( std::size_t i = 0; i < m_matchers.size(); ++i )
- if( m_matchers[i]->match( expr ) )
+
+ std::vector const*> m_matchers;
+ };
+ template
+ struct MatchAnyOf : MatcherBase {
+
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+ if (m_matchers[i]->match(arg))
return true;
+ }
return false;
}
- virtual std::string toString() const {
- std::ostringstream oss;
- oss << "( ";
+ virtual std::string describe() const CATCH_OVERRIDE {
+ std::string description;
+ description.reserve( 4 + m_matchers.size()*32 );
+ description += "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 )
- oss << " or ";
- oss << m_matchers[i]->toString();
+ description += " or ";
+ description += m_matchers[i]->toString();
}
- oss << " )";
- return oss.str();
+ description += " )";
+ return description;
}
- AnyOf operator || ( Matcher const& other ) const {
- AnyOf anyOfExpr( *this );
- anyOfExpr.add( other );
- return anyOfExpr;
+ MatchAnyOf& operator || ( MatcherBase const& other ) {
+ m_matchers.push_back( &other );
+ return *this;
}
- private:
- std::vector > > m_matchers;
+ std::vector const*> m_matchers;
};
- } // namespace Generic
+ template
+ struct MatchNotOf : MatcherBase {
- template
- Generic::AllOf Matcher::operator && ( Matcher const& other ) const {
- Generic::AllOf allOfExpr;
- allOfExpr.add( *this );
- allOfExpr.add( other );
- return allOfExpr;
- }
-
- template
- Generic::AnyOf Matcher::operator || ( Matcher const& other ) const {
- Generic::AnyOf anyOfExpr;
- anyOfExpr.add( *this );
- anyOfExpr.add( other );
- return anyOfExpr;
- }
-
- template
- Generic::Not Matcher::operator ! () const {
- return Generic::Not( *this );
- }
-
- namespace StdString {
-
- inline std::string makeString( std::string const& str ) { return str; }
- inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
-
- struct CasedString
- {
- CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
- : m_caseSensitivity( caseSensitivity ),
- m_str( adjustString( str ) )
- {}
- std::string adjustString( std::string const& str ) const {
- return m_caseSensitivity == CaseSensitive::No
- ? toLower( str )
- : str;
+ MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+ virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+ return !m_underlyingMatcher.match( arg );
}
- std::string toStringSuffix() const
- {
- return m_caseSensitivity == CaseSensitive::No
- ? " (case insensitive)"
- : std::string();
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "not " + m_underlyingMatcher.toString();
}
- CaseSensitive::Choice m_caseSensitivity;
- std::string m_str;
+ MatcherBase const& m_underlyingMatcher;
};
- struct Equals : MatcherImpl {
- Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
- : m_data( str, caseSensitivity )
- {}
- Equals( Equals const& other ) : m_data( other.m_data ){}
+ template
+ MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const {
+ return MatchAllOf() && *this && other;
+ }
+ template
+ MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const {
+ return MatchAnyOf() || *this || other;
+ }
+ template
+ MatchNotOf MatcherBase::operator ! () const {
+ return MatchNotOf( *this );
+ }
- virtual ~Equals();
-
- virtual bool match( std::string const& expr ) const {
- return m_data.m_str == m_data.adjustString( expr );;
- }
- virtual std::string toString() const {
- return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
- }
-
- CasedString m_data;
- };
-
- struct Contains : MatcherImpl {
- Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
- : m_data( substr, caseSensitivity ){}
- Contains( Contains const& other ) : m_data( other.m_data ){}
-
- virtual ~Contains();
-
- virtual bool match( std::string const& expr ) const {
- 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();
- }
-
- CasedString m_data;
- };
-
- struct StartsWith : MatcherImpl {
- StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
- : m_data( substr, caseSensitivity ){}
-
- StartsWith( StartsWith const& other ) : m_data( other.m_data ){}
-
- virtual ~StartsWith();
-
- virtual bool match( std::string const& expr ) const {
- 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();
- }
-
- CasedString m_data;
- };
-
- struct EndsWith : MatcherImpl {
- EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
- : m_data( substr, caseSensitivity ){}
- EndsWith( EndsWith const& other ) : m_data( other.m_data ){}
-
- virtual ~EndsWith();
-
- virtual bool match( std::string const& expr ) const {
- 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();
- }
-
- CasedString m_data;
- };
- } // namespace StdString
} // namespace Impl
// The following functions create the actual matcher objects.
// This allows the types to be inferred
- template
- inline Impl::Generic::Not Not( Impl::Matcher const& m ) {
- return Impl::Generic::Not( m );
+ // - deprecated: prefer ||, && and !
+ template
+ inline Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) {
+ return Impl::MatchNotOf( underlyingMatcher );
}
-
- template
- inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1,
- Impl::Matcher const& m2 ) {
- return Impl::Generic::AllOf().add( m1 ).add( m2 );
+ template
+ inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) {
+ return Impl::MatchAllOf() && m1 && m2;
}
- template
- inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1,
- Impl::Matcher const& m2,
- Impl::Matcher const& m3 ) {
- return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 );
+ template
+ inline Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) {
+ return Impl::MatchAllOf() && m1 && m2 && m3;
}
- template
- inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1,
- Impl::Matcher const& m2 ) {
- return Impl::Generic::AnyOf().add( m1 ).add( m2 );
+ template
+ inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) {
+ return Impl::MatchAnyOf() || m1 || m2;
}
- template
- inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1,
- Impl::Matcher const& m2,
- Impl::Matcher const& m3 ) {
- return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 );
- }
-
- inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
- return Impl::StdString::Equals( str, caseSensitivity );
- }
- inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
- return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity );
- }
- inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
- return Impl::StdString::Contains( substr, caseSensitivity );
- }
- inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
- return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity );
- }
- inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) {
- return Impl::StdString::StartsWith( substr );
- }
- inline Impl::StdString::StartsWith StartsWith( const char* substr ) {
- return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
- }
- inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) {
- return Impl::StdString::EndsWith( substr );
- }
- inline Impl::StdString::EndsWith EndsWith( const char* substr ) {
- return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+ template
+ inline Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) {
+ return Impl::MatchAnyOf() || m1 || m2 || m3;
}
} // namespace Matchers
using namespace Matchers;
+using Matchers::Impl::MatcherBase;
} // namespace Catch
@@ -1328,7 +1205,7 @@ namespace Catch {
void captureResult( ResultWas::OfType resultType );
void captureExpression();
void captureExpectedException( std::string const& expectedMessage );
- void captureExpectedException( Matchers::Impl::Matcher const& matcher );
+ void captureExpectedException( Matchers::Impl::MatcherBase const& matcher );
void handleResult( AssertionResult const& result );
void react();
bool shouldDebugBreak() const;
@@ -1358,6 +1235,7 @@ namespace Catch {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
#endif
#include
@@ -1874,6 +1752,8 @@ class ExpressionLhs : public DecomposedExpression {
public:
ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+ ExpressionLhs& operator = ( const ExpressionLhs& );
+
template
BinaryExpression
operator == ( RhsT const& rhs ) {
@@ -1952,6 +1832,8 @@ public:
BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
: m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+ BinaryExpression& operator = ( BinaryExpression& );
+
void endExpression() const {
m_rb
.setResultType( Internal::compare( m_lhs, m_rhs ) )
@@ -2246,6 +2128,14 @@ namespace Catch {
}
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+ resultBuilder.react();
+#else
///////////////////////////////////////////////////////////////////////////////
// In the event of a failure works out if the debugger needs to be invoked
// and/or an exception thrown and takes appropriate action.
@@ -2254,6 +2144,7 @@ namespace Catch {
#define INTERNAL_CATCH_REACT( resultBuilder ) \
if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
resultBuilder.react();
+#endif
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
@@ -2262,6 +2153,7 @@ namespace Catch {
try { \
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
( __catchResult <= expr ).endExpression(); \
+ CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
} \
catch( ... ) { \
__catchResult.useActiveException( resultDisposition ); \
@@ -2825,12 +2717,14 @@ namespace Detail {
public:
explicit Approx ( double value )
: m_epsilon( std::numeric_limits::epsilon()*100 ),
+ m_margin( 0.0 ),
m_scale( 1.0 ),
m_value( value )
{}
Approx( Approx const& other )
: m_epsilon( other.m_epsilon ),
+ m_margin( other.m_margin ),
m_scale( other.m_scale ),
m_value( other.m_value )
{}
@@ -2842,6 +2736,7 @@ namespace Detail {
Approx operator()( double value ) {
Approx approx( value );
approx.epsilon( m_epsilon );
+ approx.margin( m_margin );
approx.scale( m_scale );
return approx;
}
@@ -2851,7 +2746,11 @@ namespace Detail {
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 std::fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs_v), std::fabs(rhs.m_value) ) );
+ bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
}
template ::value>::type>
@@ -2895,7 +2794,11 @@ namespace Detail {
#else
friend bool operator == ( double lhs, Approx const& rhs ) {
// Thanks to Richard Harris for his help refining this formula
- return std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+ bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+ if (relativeOK) {
+ return true;
+ }
+ return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
}
friend bool operator == ( Approx const& lhs, double rhs ) {
@@ -2936,6 +2839,11 @@ namespace Detail {
return *this;
}
+ Approx& margin( double newMargin ) {
+ m_margin = newMargin;
+ return *this;
+ }
+
Approx& scale( double newScale ) {
m_scale = newScale;
return *this;
@@ -2949,6 +2857,7 @@ namespace Detail {
private:
double m_epsilon;
+ double m_margin;
double m_scale;
double m_value;
};
@@ -2961,6 +2870,153 @@ inline std::string toString( Detail::Approx const& value ) {
} // end namespace Catch
+// #included from: internal/catch_matchers_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ struct CasedString
+ {
+ CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+ std::string adjustString( std::string const& str ) const;
+ std::string caseSensitivitySuffix() const;
+
+ CaseSensitive::Choice m_caseSensitivity;
+ std::string m_str;
+ };
+
+ struct StringMatcherBase : MatcherBase {
+ StringMatcherBase( std::string operation, CasedString const& comparator );
+ virtual std::string describe() const CATCH_OVERRIDE;
+
+ CasedString m_comparator;
+ std::string m_operation;
+ };
+
+ struct EqualsMatcher : StringMatcherBase {
+ EqualsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct ContainsMatcher : StringMatcherBase {
+ ContainsMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct StartsWithMatcher : StringMatcherBase {
+ StartsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+ struct EndsWithMatcher : StringMatcherBase {
+ EndsWithMatcher( CasedString const& comparator );
+ virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+ };
+
+ } // namespace StdString
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+ namespace Vector {
+
+ template
+ struct ContainsElementMatcher : MatcherBase, T> {
+
+ ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+ bool match(std::vector const &v) const CATCH_OVERRIDE {
+ return std::find(v.begin(), v.end(), m_comparator) != v.end();
+ }
+
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ T const& m_comparator;
+ };
+
+ template
+ struct ContainsMatcher : MatcherBase, std::vector > {
+
+ ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector const &v) const CATCH_OVERRIDE {
+ // !TBD: see note in EqualsMatcher
+ if (m_comparator.size() > v.size())
+ return false;
+ for (size_t i = 0; i < m_comparator.size(); ++i)
+ if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Contains: " + Catch::toString( m_comparator );
+ }
+
+ std::vector const& m_comparator;
+ };
+
+ template
+ struct EqualsMatcher : MatcherBase, std::vector > {
+
+ EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {}
+
+ bool match(std::vector const &v) const CATCH_OVERRIDE {
+ // !TBD: This currently works if all elements can be compared using !=
+ // - a more general approach would be via a compare template that defaults
+ // to using !=. but could be specialised for, e.g. std::vector etc
+ // - then just call that directly
+ if (m_comparator.size() != v.size())
+ return false;
+ for (size_t i = 0; i < v.size(); ++i)
+ if (m_comparator[i] != v[i])
+ return false;
+ return true;
+ }
+ virtual std::string describe() const CATCH_OVERRIDE {
+ return "Equals: " + Catch::toString( m_comparator );
+ }
+ std::vector const& m_comparator;
+ };
+
+ } // namespace Vector
+
+ // The following functions create the actual matcher objects.
+ // This allows the types to be inferred
+
+ template
+ Vector::ContainsMatcher Contains( std::vector const& comparator ) {
+ return Vector::ContainsMatcher( comparator );
+ }
+
+ template
+ Vector::ContainsElementMatcher VectorContains( T const& comparator ) {
+ return Vector::ContainsElementMatcher( comparator );
+ }
+
+ template
+ Vector::EqualsMatcher Equals( std::vector const& comparator ) {
+ return Vector::EqualsMatcher( comparator );
+ }
+
+} // namespace Matchers
+} // namespace Catch
+
// #included from: internal/catch_interfaces_tag_alias_registry.h
#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
@@ -3342,6 +3398,29 @@ return @ desc; \
#endif
#ifdef CATCH_IMPL
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include
+class LeakDetector {
+public:
+ LeakDetector() {
+ int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ flag |= _CRTDBG_LEAK_CHECK_DF;
+ flag |= _CRTDBG_ALLOC_MEM_DF;
+ _CrtSetDbgFlag(flag);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ // Change this to leaking allocation's number to break there
+ _CrtSetBreakAlloc(-1);
+ }
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
// #included from: internal/catch_impl.hpp
#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
@@ -3601,7 +3680,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]-i ) + token.substr( m_escapeChars[i]+1-i );
+ token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
m_escapeChars.clear();
if( startsWith( token, "exclude:" ) ) {
m_exclusion = true;
@@ -6259,6 +6338,16 @@ namespace Catch {
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
#include
namespace Catch {
@@ -6337,6 +6426,8 @@ namespace Catch {
} // namespace Catch
+# endif // CATCH_CONFIG_POSIX_SIGNALS
+
#endif // not Windows
#include
@@ -8043,7 +8134,7 @@ namespace Catch {
return os;
}
- Version libraryVersion( 1, 7, 2, "", 0 );
+ Version libraryVersion( 1, 8, 0, "", 0 );
}
@@ -8214,7 +8305,15 @@ namespace Catch
#endif
#ifdef CATCH_PLATFORM_WINDOWS
+
#else
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# ifdef __CYGWIN__
+# define _BSD_SOURCE
+# endif
+
#include
#endif
@@ -8768,12 +8867,12 @@ namespace Catch {
void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
if( expectedMessage.empty() )
- captureExpectedException( Matchers::Impl::Generic::AllOf() );
+ captureExpectedException( Matchers::Impl::MatchAllOf() );
else
captureExpectedException( Matchers::Equals( expectedMessage ) );
}
- void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) {
+ void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) {
assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
AssertionResultData data = m_data;
@@ -8807,6 +8906,15 @@ namespace Catch {
}
void ResultBuilder::react() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+ 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 throw statement
+ ///////////////////////////////////////////////////////////////////
+ CATCH_BREAK_INTO_DEBUGGER();
+ }
+#endif
if( m_shouldThrow )
throw Catch::TestFailureException();
}
@@ -8935,6 +9043,86 @@ namespace Catch {
} // end namespace Catch
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+ namespace StdString {
+
+ CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+ : m_caseSensitivity( caseSensitivity ),
+ m_str( adjustString( str ) )
+ {}
+ std::string CasedString::adjustString( std::string const& str ) const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? toLower( str )
+ : str;
+ }
+ std::string CasedString::caseSensitivitySuffix() const {
+ return m_caseSensitivity == CaseSensitive::No
+ ? " (case insensitive)"
+ : std::string();
+ }
+
+ StringMatcherBase::StringMatcherBase( std::string operation, CasedString const& comparator )
+ : m_comparator( comparator ),
+ m_operation( operation ) {
+ }
+
+ std::string StringMatcherBase::describe() const {
+ std::string description;
+ description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+ m_comparator.caseSensitivitySuffix().size());
+ description += m_operation;
+ description += ": \"";
+ description += m_comparator.m_str;
+ description += "\"";
+ description += m_comparator.caseSensitivitySuffix();
+ return description;
+ }
+
+ EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+ bool EqualsMatcher::match( std::string const& source ) const {
+ return m_comparator.adjustString( source ) == m_comparator.m_str;
+ }
+
+ ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+ bool ContainsMatcher::match( std::string const& source ) const {
+ return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+ bool StartsWithMatcher::match( std::string const& source ) const {
+ return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+ bool EndsWithMatcher::match( std::string const& source ) const {
+ return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+ }
+
+ } // namespace StdString
+
+ StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+ StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+ return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+ }
+
+} // namespace Matchers
+} // namespace Catch
// #included from: ../reporters/catch_reporter_multi.hpp
#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
@@ -9078,6 +9266,7 @@ Ptr addReporter( Ptr const& existingRepo
#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
#include
+#include
namespace Catch {
@@ -9677,6 +9866,12 @@ namespace Catch {
return std::string();
}
+ void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+ m_xml
+ .writeAttribute( "filename", sourceInfo.file )
+ .writeAttribute( "line", sourceInfo.line );
+ }
+
public: // StreamingReporterBase
virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
@@ -9706,6 +9901,8 @@ namespace Catch {
.writeAttribute( "description", testInfo.description )
.writeAttribute( "tags", testInfo.tagsAsString );
+ writeSourceInfo( testInfo.lineInfo );
+
if ( m_config->showDurations() == ShowDurations::Always )
m_testCaseTimer.start();
m_xml.ensureTagClosed();
@@ -9717,6 +9914,7 @@ namespace Catch {
m_xml.startElement( "Section" )
.writeAttribute( "name", trim( sectionInfo.name ) )
.writeAttribute( "description", sectionInfo.description );
+ writeSourceInfo( sectionInfo.lineInfo );
m_xml.ensureTagClosed();
}
}
@@ -9749,9 +9947,9 @@ namespace Catch {
if( assertionResult.hasExpression() ) {
m_xml.startElement( "Expression" )
.writeAttribute( "success", assertionResult.succeeded() )
- .writeAttribute( "type", assertionResult.getTestMacroName() )
- .writeAttribute( "filename", assertionResult.getSourceInfo().file )
- .writeAttribute( "line", assertionResult.getSourceInfo().line );
+ .writeAttribute( "type", assertionResult.getTestMacroName() );
+
+ writeSourceInfo( assertionResult.getSourceInfo() );
m_xml.scopedElement( "Original" )
.writeText( assertionResult.getExpression() );
@@ -9762,16 +9960,16 @@ namespace Catch {
// And... Print a result applicable to each result type.
switch( assertionResult.getResultType() ) {
case ResultWas::ThrewException:
- m_xml.scopedElement( "Exception" )
- .writeAttribute( "filename", assertionResult.getSourceInfo().file )
- .writeAttribute( "line", assertionResult.getSourceInfo().line )
- .writeText( assertionResult.getMessage() );
+ m_xml.startElement( "Exception" );
+ writeSourceInfo( assertionResult.getSourceInfo() );
+ m_xml.writeText( assertionResult.getMessage() );
+ m_xml.endElement();
break;
case ResultWas::FatalErrorCondition:
- m_xml.scopedElement( "FatalErrorCondition" )
- .writeAttribute( "filename", assertionResult.getSourceInfo().file )
- .writeAttribute( "line", assertionResult.getSourceInfo().line )
- .writeText( assertionResult.getMessage() );
+ m_xml.startElement( "FatalErrorCondition" );
+ writeSourceInfo( assertionResult.getSourceInfo() );
+ m_xml.writeText( assertionResult.getMessage() );
+ m_xml.endElement();
break;
case ResultWas::Info:
m_xml.scopedElement( "Info" )
@@ -9781,8 +9979,10 @@ namespace Catch {
// Warning will already have been written
break;
case ResultWas::ExplicitFailure:
- m_xml.scopedElement( "Failure" )
- .writeText( assertionResult.getMessage() );
+ m_xml.startElement( "Failure" );
+ writeSourceInfo( assertionResult.getSourceInfo() );
+ m_xml.writeText( assertionResult.getMessage() );
+ m_xml.endElement();
break;
default:
break;
@@ -10095,8 +10295,30 @@ namespace Catch {
// #included from: ../reporters/catch_reporter_console.hpp
#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+#include
+#include
+
namespace Catch {
+ namespace {
+ // Because formatting using c++ streams is stateful, drop down to C is required
+ // Alternatively we could use stringstream, but its performance is... not good.
+ std::string getFormattedDuration( double duration ) {
+ // Max exponent + 1 is required to represent the whole part
+ // + 1 for decimal point
+ // + 3 for the 3 decimal places
+ // + 1 for null terminator
+ const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+ char buffer[maxDoubleSize];
+#ifdef _MSC_VER
+ sprintf_s(buffer, "%.3f", duration);
+#else
+ sprintf(buffer, "%.3f", duration);
+#endif
+ return std::string(buffer);
+ }
+ }
+
struct ConsoleReporter : StreamingReporterBase {
ConsoleReporter( ReporterConfig const& _config )
: StreamingReporterBase( _config ),
@@ -10149,14 +10371,11 @@ namespace Catch {
stream << "\nNo assertions in test case";
stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
}
- if( m_headerPrinted ) {
- if( m_config->showDurations() == ShowDurations::Always )
- stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
- m_headerPrinted = false;
+ if( m_config->showDurations() == ShowDurations::Always ) {
+ stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
}
- else {
- if( m_config->showDurations() == ShowDurations::Always )
- stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
+ if( m_headerPrinted ) {
+ m_headerPrinted = false;
}
StreamingReporterBase::sectionEnded( _sectionStats );
}
@@ -10860,11 +11079,6 @@ namespace Catch {
TestSpec::TagPattern::~TagPattern() {}
TestSpec::ExcludedPattern::~ExcludedPattern() {}
- Matchers::Impl::StdString::Equals::~Equals() {}
- Matchers::Impl::StdString::Contains::~Contains() {}
- Matchers::Impl::StdString::StartsWith::~StartsWith() {}
- Matchers::Impl::StdString::EndsWith::~EndsWith() {}
-
void Config::dummy() {}
namespace TestCaseTracking {
@@ -10889,7 +11103,7 @@ namespace Catch {
// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
- int result = Catch::Session().run( argc, argv );
+ int result = Catch::Session().run( argc, argv );
return ( result < 0xff ? result : 0xff );
}