diff options
-rw-r--r-- | test/unit/catch.hpp | 5037 |
1 files changed, 3048 insertions, 1989 deletions
diff --git a/test/unit/catch.hpp b/test/unit/catch.hpp index 2fc6b728e..03665c43f 100644 --- a/test/unit/catch.hpp +++ b/test/unit/catch.hpp @@ -1,6 +1,6 @@ /* - * CATCH v1.0 build 13 (master branch) - * Generated: 2013-11-13 08:10:05.836093 + * CATCH v1.0 build 43 (master branch) + * Generated: 2014-05-04 09:22:51.466832 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -16,9 +16,22 @@ #ifdef __clang__ #pragma clang diagnostic ignored "-Wglobal-constructors" #pragma clang diagnostic ignored "-Wvariadic-macros" - +#pragma clang diagnostic ignored "-Wc99-extensions" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif + +#ifdef CATCH_CONFIG_MAIN +# define CATCH_CONFIG_RUNNER +#endif + +#ifdef CATCH_CONFIG_RUNNER +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif #endif // #included from: internal/catch_notimplemented_exception.h @@ -43,6 +56,18 @@ // Much of the following code is based on Boost (1.53) +#ifdef __clang__ + +# if __has_feature(cxx_nullptr) +# define CATCH_CONFIG_CPP11_NULLPTR +# endif + +# if __has_feature(cxx_noexcept) +# define CATCH_CONFIG_CPP11_NOEXCEPT +# endif + +#endif // __clang__ + //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ @@ -89,6 +114,11 @@ #endif // __GNUC__ < 3 +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) + +#define CATCH_CONFIG_CPP11_NULLPTR +#endif + #endif // __GNUC__ //////////////////////////////////////////////////////////////////////////////// @@ -113,6 +143,26 @@ #endif +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// detect language version: +#if (__cplusplus == 201103L) +# define CATCH_CPP11 +# define CATCH_CPP11_OR_GREATER +#elif (__cplusplus >= 201103L) +# define CATCH_CPP11_OR_GREATER +#endif + +// noexcept support: +#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) +# define CATCH_NOEXCEPT noexcept +# define CATCH_NOEXCEPT_IS(x) noexcept(x) +#else +# define CATCH_NOEXCEPT throw() +# define CATCH_NOEXCEPT_IS(x) +#endif + namespace Catch { class NonCopyable { @@ -149,46 +199,17 @@ namespace Catch { delete it->second; } - template<typename ContainerT, typename Function> - inline void forEach( ContainerT& container, Function function ) { - std::for_each( container.begin(), container.end(), function ); - } - - template<typename ContainerT, typename Function> - inline void forEach( ContainerT const& container, Function function ) { - std::for_each( container.begin(), container.end(), function ); - } - - inline bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; - } - inline bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; - } - inline bool contains( std::string const& s, std::string const& infix ) { - return s.find( infix ) != std::string::npos; - } - inline void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), ::tolower ); - } - inline std::string toLower( std::string const& s ) { - std::string lc = s; - toLowerInPlace( lc ); - return lc; - } + bool startsWith( std::string const& s, std::string const& prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); struct pluralise { - pluralise( std::size_t count, std::string const& label ) - : m_count( count ), - m_label( label ) - {} + pluralise( std::size_t count, std::string const& label ); - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; - if( pluraliser.m_count != 1 ) - os << "s"; - return os; - } + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); std::size_t m_count; std::string m_label; @@ -196,42 +217,40 @@ namespace Catch { struct SourceLineInfo { - SourceLineInfo() : line( 0 ){} - SourceLineInfo( std::string const& _file, std::size_t _line ) - : file( _file ), - line( _line ) - {} - SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} - bool empty() const { - return file.empty(); - } - bool operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; - } + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); + SourceLineInfo( SourceLineInfo const& other ); +# ifdef CATCH_CPP11_OR_GREATER + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; +# endif + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + std::string file; std::size_t line; }; - inline std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; -#else - os << info.file << ":" << info.line; -#endif - return os; - } + 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 isTrue( bool value ){ return value; } - inline void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; - if( isTrue( true )) - throw std::logic_error( oss.str() ); + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template<typename T> + T const& operator + ( T const& value, StreamEndStop ) { + return value; } } @@ -247,9 +266,9 @@ namespace Catch { public: NotImplementedException( SourceLineInfo const& lineInfo ); - virtual ~NotImplementedException() throw() {} + virtual ~NotImplementedException() CATCH_NOEXCEPT {} - virtual const char* what() const throw(); + virtual const char* what() const CATCH_NOEXCEPT; private: std::string m_what; @@ -430,11 +449,14 @@ namespace Catch { }; class TestCase; + struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector<TestCase> const& getAllTests() const = 0; - virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const = 0; + virtual void getFilteredTests( TestCaseFilters const& filters, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0; + virtual void getFilteredTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) const = 0; + }; } @@ -555,8 +577,8 @@ private: // #included from: catch_expressionresult_builder.h #define TWOBLUECUBES_CATCH_ASSERTIONRESULT_BUILDER_H_INCLUDED -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED +// #included from: catch_tostring.h +#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED // #included from: catch_sfinae.hpp #define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED @@ -596,6 +618,7 @@ namespace Catch { #include <iomanip> #include <limits> #include <vector> +#include <cstddef> #ifdef __OBJC__ // #included from: catch_objc_arc.hpp @@ -702,6 +725,43 @@ namespace Detail { } }; + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union { + int asInt; + char asChar[sizeof (int)]; + }; + + asInt = 1; + return ( asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + + // Writes the raw memory into a string, considering endianness + template<typename T> + std::string rawMemoryToString( T value ) { + union { + T typedValue; + unsigned char bytes[sizeof(T)]; + }; + + typedValue = value; + + std::ostringstream oss; + oss << "0x"; + + int i = 0, end = sizeof(T), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + for( ; i != end; i += inc ) + oss << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)bytes[i]; + return oss.str(); + } + } // end namespace Detail template<typename T> @@ -717,9 +777,18 @@ struct StringMaker<T*> { static std::string convert( U* p ) { if( !p ) return INTERNAL_CATCH_STRINGIFY( NULL ); - std::ostringstream oss; - oss << p; - return oss.str(); + else + return Detail::rawMemoryToString( p ); + } +}; + +template<typename R, typename C> +struct StringMaker<R C::*> { + static std::string convert( R C::* p ) { + if( !p ) + return INTERNAL_CATCH_STRINGIFY( NULL ); + else + return Detail::rawMemoryToString( p ); } }; @@ -737,7 +806,7 @@ struct StringMaker<std::vector<T, Allocator> > { namespace Detail { template<typename T> - inline std::string makeString( T const& value ) { + std::string makeString( T const& value ) { return StringMaker<T>::convert( value ); } } // end namespace Detail @@ -756,99 +825,27 @@ std::string toString( T const& value ) { // Built in overloads -inline std::string toString( std::string const& value ) { - return "\"" + value + "\""; -} - -inline std::string toString( std::wstring const& value ) { - std::ostringstream oss; - oss << "\""; - for(size_t i = 0; i < value.size(); ++i ) - oss << static_cast<char>( value[i] <= 0xff ? value[i] : '?'); - oss << "\""; - return oss.str(); -} - -inline std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} - -inline std::string toString( char* const value ) { - return Catch::toString( static_cast<const char*>( value ) ); -} - -inline std::string toString( int value ) { - std::ostringstream oss; - oss << value; - return oss.str(); -} - -inline std::string toString( unsigned long value ) { - std::ostringstream oss; - if( value > 8192 ) - oss << "0x" << std::hex << value; - else - oss << value; - return oss.str(); -} - -inline std::string toString( unsigned int value ) { - return toString( static_cast<unsigned long>( value ) ); -} - -inline std::string toString( const double value ) { - std::ostringstream oss; - oss << std::setprecision( 10 ) - << std::fixed - << value; - std::string d = oss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); - } - return d; -} - -inline std::string toString( bool value ) { - return value ? "true" : "false"; -} - -inline std::string toString( char value ) { - return value < ' ' - ? toString( static_cast<unsigned int>( value ) ) - : Detail::makeString( value ); -} - -inline std::string toString( signed char value ) { - return toString( static_cast<char>( value ) ); -} - -inline std::string toString( unsigned char value ) { - return toString( static_cast<char>( value ) ); -} +std::string toString( std::string const& value ); +std::string toString( std::wstring const& value ); +std::string toString( const char* const value ); +std::string toString( char* const value ); +std::string toString( int value ); +std::string toString( unsigned long value ); +std::string toString( unsigned int value ); +std::string toString( const double value ); +std::string toString( bool value ); +std::string toString( char value ); +std::string toString( signed char value ); +std::string toString( unsigned char value ); #ifdef CATCH_CONFIG_CPP11_NULLPTR -inline std::string toString( std::nullptr_t ) { - return "nullptr"; -} +std::string toString( std::nullptr_t ); #endif #ifdef __OBJC__ - inline std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return std::string( "@\"" ) + [nsstring UTF8String] + "\""; - } - inline std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { - if( !nsstring ) - return "nil"; - return std::string( "@\"" ) + [nsstring UTF8String] + "\""; - } - inline std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } + std::string toString( NSString const * const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSObject* const& nsObject ); #endif namespace Detail { @@ -962,6 +959,12 @@ namespace Catch { AssertionResult(); AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); ~AssertionResult(); +# ifdef CATCH_CPP11_OR_GREATER + AssertionResult( AssertionResult const& ) = default; + AssertionResult( AssertionResult && ) = default; + AssertionResult& operator = ( AssertionResult const& ) = default; + AssertionResult& operator = ( AssertionResult && ) = default; +# endif bool isOk() const; bool succeeded() const; @@ -991,6 +994,8 @@ namespace Catch { #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #endif +#include <cstddef> + namespace Catch { namespace Internal { @@ -1200,14 +1205,21 @@ private: namespace Catch { -// Wraps the LHS of an expression and captures the operator and RHS (if any) - wrapping them all -// in an ExpressionResultBuilder object +// Wraps the LHS of an expression and captures the operator and RHS (if any) - +// wrapping them all in an ExpressionResultBuilder object template<typename T> class ExpressionLhs { - void operator = ( ExpressionLhs const& ); + ExpressionLhs& operator = ( ExpressionLhs const& ); +# ifdef CATCH_CPP11_OR_GREATER + ExpressionLhs& operator = ( ExpressionLhs && ) = delete; +# endif public: ExpressionLhs( T lhs ) : m_lhs( lhs ) {} +# ifdef CATCH_CPP11_OR_GREATER + ExpressionLhs( ExpressionLhs const& ) = default; + ExpressionLhs( ExpressionLhs && ) = default; +# endif template<typename RhsT> ExpressionResultBuilder& operator == ( RhsT const& rhs ) { @@ -1358,65 +1370,6 @@ namespace Catch { #define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED #include <string> -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED - -#include <cstddef> - -namespace Catch { - - struct Counts { - Counts() : passed( 0 ), failed( 0 ) {} - - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - return *this; - } - - std::size_t total() const { - return passed + failed; - } - - std::size_t passed; - std::size_t failed; - }; - - struct Totals { - - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } - - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } - - Counts assertions; - Counts testCases; - }; -} - namespace Catch { @@ -1427,6 +1380,7 @@ namespace Catch { struct SectionInfo; struct MessageInfo; class ScopedMessageBuilder; + struct Counts; struct IResultCapture { @@ -1448,10 +1402,8 @@ namespace Catch { }; } -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED - -#include <iostream> +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED // #included from: catch_platform.h #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED @@ -1464,516 +1416,74 @@ namespace Catch { #define CATCH_PLATFORM_WINDOWS #endif -#ifdef CATCH_PLATFORM_MAC - - #include <assert.h> - #include <stdbool.h> - #include <sys/types.h> - #include <unistd.h> - #include <sys/sysctl.h> - - namespace Catch{ - - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - inline bool isDebuggerActive(){ - - int junk; - int mib[4]; - struct kinfo_proc info; - size_t size; - - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - - info.kp_proc.p_flag = 0; - - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); +#include <string> - // Call sysctl. +namespace Catch{ - size = sizeof(info); - junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); - assert(junk == 0); - - // We're being debugged if the P_TRACED flag is set. + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); +} - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); - } - } +#ifdef CATCH_PLATFORM_MAC - // The following code snippet taken from: + // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html #ifdef DEBUG #if defined(__ppc64__) || defined(__ppc__) - #define BreakIntoDebugger() \ - if( Catch::isDebuggerActive() ) { \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ); \ - } + #define CATCH_BREAK_INTO_DEBUGGER() \ + if( Catch::isDebuggerActive() ) { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ); \ + } #else - #define BreakIntoDebugger() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} #endif - #else - inline void BreakIntoDebugger(){} #endif #elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - #define BreakIntoDebugger() if (IsDebuggerPresent() ) { __debugbreak(); } - inline bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } #elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define BreakIntoDebugger() if (IsDebuggerPresent() ) { DebugBreak(); } - inline bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } -#else - inline void BreakIntoDebugger(){} - inline bool isDebuggerActive() { return false; } + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } #endif -#ifdef CATCH_PLATFORM_WINDOWS -extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); -inline void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); -} -#else -inline void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - std::cout << text; -} -#endif // CATCH_PLATFORM_WINDOWS +#ifndef CATCH_BREAK_INTO_DEBUGGER +#define CATCH_BREAK_INTO_DEBUGGER() Catch::isTrue( true ); +#endif // #included from: catch_interfaces_registry_hub.h #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED - -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED - -// #included from: catch_test_spec.h -#define TWOBLUECUBES_CATCH_TEST_SPEC_H_INCLUDED - -// #included from: catch_test_case_info.h -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED - #include <string> -#include <set> - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif namespace Catch { - struct ITestCase; - - struct TestCaseInfo { - TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set<std::string> const& _tags, - bool _isHidden, - SourceLineInfo const& _lineInfo ); - - TestCaseInfo( TestCaseInfo const& other ); - - std::string name; - std::string className; - std::string description; - std::set<std::string> tags; - std::string tagsAsString; - SourceLineInfo lineInfo; - bool isHidden; - }; - - class TestCase : protected TestCaseInfo { - public: - - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); - - TestCase withName( std::string const& _newName ) const; - - void invoke() const; - - TestCaseInfo const& getTestCaseInfo() const; - - bool isHidden() const; - bool hasTag( std::string const& tag ) const; - bool matchesTags( std::string const& tagPattern ) const; - std::set<std::string> const& getTags() const; - - void swap( TestCase& other ); - bool operator == ( TestCase const& other ) const; - bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); - - private: - Ptr<ITestCase> test; - }; - - TestCase makeTestCase( ITestCase* testCase, - std::string const& className, - std::string const& name, - std::string const& description, - SourceLineInfo const& lineInfo ); -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -// #included from: catch_tags.hpp -#define TWOBLUECUBES_CATCH_TAGS_HPP_INCLUDED - -#include <string> -#include <set> -#include <map> -#include <vector> - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - class TagParser { - public: - virtual ~TagParser(); - - void parse( std::string const& str ) { - std::size_t pos = 0; - while( pos < str.size() ) { - char c = str[pos]; - if( c == '[' ) { - std::size_t end = str.find_first_of( ']', pos ); - if( end != std::string::npos ) { - acceptTag( str.substr( pos+1, end-pos-1 ) ); - pos = end+1; - } - else { - acceptChar( c ); - pos++; - } - } - else { - acceptChar( c ); - pos++; - } - } - endParse(); - } - - protected: - virtual void acceptTag( std::string const& tag ) = 0; - virtual void acceptChar( char c ) = 0; - virtual void endParse() {} - - private: - }; - - class TagExtracter : public TagParser { - public: - - TagExtracter( std::set<std::string>& tags ) - : m_tags( tags ) - {} - virtual ~TagExtracter(); - - void parse( std::string& description ) { - TagParser::parse( description ); - description = m_remainder; - } - - private: - virtual void acceptTag( std::string const& tag ) { - m_tags.insert( toLower( tag ) ); - } - virtual void acceptChar( char c ) { - m_remainder += c; - } - - TagExtracter& operator=(TagExtracter const&); - - std::set<std::string>& m_tags; - std::string m_remainder; - }; - - class Tag { - public: - Tag() - : m_isNegated( false ) - {} - - Tag( std::string const& name, bool isNegated ) - : m_name( name ), - m_isNegated( isNegated ) - {} - - std::string getName() const { - return m_name; - } - bool isNegated() const { - return m_isNegated; - } - - bool operator ! () const { - return m_name.empty(); - } - - private: - std::string m_name; - bool m_isNegated; - }; - - class TagSet { - typedef std::map<std::string, Tag> TagMap; - public: - void add( Tag const& tag ) { - m_tags.insert( std::make_pair( toLower( tag.getName() ), tag ) ); - } - - bool empty() const { - return m_tags.empty(); - } - - bool matches( std::set<std::string> const& tags ) const { - for( TagMap::const_iterator - it = m_tags.begin(), itEnd = m_tags.end(); - it != itEnd; - ++it ) { - bool found = tags.find( it->first ) != tags.end(); - if( found == it->second.isNegated() ) - return false; - } - return true; - } - - private: - TagMap m_tags; - }; - - class TagExpression { - public: - bool matches( std::set<std::string> const& tags ) const { - for( std::vector<TagSet>::const_iterator - it = m_tagSets.begin(), itEnd = m_tagSets.end(); - it != itEnd; - ++it ) - if( it->matches( tags ) ) - return true; - return false; - } - - private: - friend class TagExpressionParser; - - std::vector<TagSet> m_tagSets; - }; - - class TagExpressionParser : public TagParser { - public: - TagExpressionParser( TagExpression& exp ) - : m_isNegated( false ), - m_exp( exp ) - {} - - ~TagExpressionParser(); - - private: - virtual void acceptTag( std::string const& tag ) { - m_currentTagSet.add( Tag( tag, m_isNegated ) ); - m_isNegated = false; - } - virtual void acceptChar( char c ) { - switch( c ) { - case '~': - m_isNegated = true; - break; - case ',': - m_exp.m_tagSets.push_back( m_currentTagSet ); - m_currentTagSet = TagSet(); - break; - } - } - virtual void endParse() { - if( !m_currentTagSet.empty() ) - m_exp.m_tagSets.push_back( m_currentTagSet ); - } + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; - TagExpressionParser& operator=(TagExpressionParser const&); + struct IRegistryHub { + virtual ~IRegistryHub(); - bool m_isNegated; - TagSet m_currentTagSet; - TagExpression& m_exp; + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; -} // end namespace Catch - -#include <string> -#include <vector> - -namespace Catch { - - struct IfFilterMatches{ enum DoWhat { - AutoDetectBehaviour, - IncludeTests, - ExcludeTests - }; }; - - class TestCaseFilter { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - TestCaseFilter( std::string const& testSpec, IfFilterMatches::DoWhat matchBehaviour = IfFilterMatches::AutoDetectBehaviour ) - : m_stringToMatch( toLower( testSpec ) ), - m_filterType( matchBehaviour ), - m_wildcardPosition( NoWildcard ) - { - if( m_filterType == IfFilterMatches::AutoDetectBehaviour ) { - if( startsWith( m_stringToMatch, "exclude:" ) ) { - m_stringToMatch = m_stringToMatch.substr( 8 ); - m_filterType = IfFilterMatches::ExcludeTests; - } - else if( startsWith( m_stringToMatch, "~" ) ) { - m_stringToMatch = m_stringToMatch.substr( 1 ); - m_filterType = IfFilterMatches::ExcludeTests; - } - else { - m_filterType = IfFilterMatches::IncludeTests; - } - } - - if( startsWith( m_stringToMatch, "*" ) ) { - m_stringToMatch = m_stringToMatch.substr( 1 ); - m_wildcardPosition = (WildcardPosition)( m_wildcardPosition | WildcardAtStart ); - } - if( endsWith( m_stringToMatch, "*" ) ) { - m_stringToMatch = m_stringToMatch.substr( 0, m_stringToMatch.size()-1 ); - m_wildcardPosition = (WildcardPosition)( m_wildcardPosition | WildcardAtEnd ); - } - } - - IfFilterMatches::DoWhat getFilterType() const { - return m_filterType; - } - - bool shouldInclude( TestCase const& testCase ) const { - return isMatch( testCase ) == (m_filterType == IfFilterMatches::IncludeTests); - } - private: - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - - bool isMatch( TestCase const& testCase ) const { - std::string name = testCase.getTestCaseInfo().name; - toLowerInPlace( name ); - - switch( m_wildcardPosition ) { - case NoWildcard: - return m_stringToMatch == name; - case WildcardAtStart: - return endsWith( name, m_stringToMatch ); - case WildcardAtEnd: - return startsWith( name, m_stringToMatch ); - case WildcardAtBothEnds: - return contains( name, m_stringToMatch ); - } - throw std::logic_error( "Unhandled wildcard type" ); - } - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - std::string m_stringToMatch; - IfFilterMatches::DoWhat m_filterType; - WildcardPosition m_wildcardPosition; + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; }; - class TestCaseFilters { - public: - TestCaseFilters( std::string const& name ) : m_name( name ) {} - - std::string getName() const { - return m_name; - } - - void addFilter( TestCaseFilter const& filter ) { - if( filter.getFilterType() == IfFilterMatches::ExcludeTests ) - m_exclusionFilters.push_back( filter ); - else - m_inclusionFilters.push_back( filter ); - } - - void addTags( std::string const& tagPattern ) { - TagExpression exp; - TagExpressionParser( exp ).parse( tagPattern ); - - m_tagExpressions.push_back( exp ); - } - - bool shouldInclude( TestCase const& testCase ) const { - if( !m_tagExpressions.empty() ) { - std::vector<TagExpression>::const_iterator it = m_tagExpressions.begin(); - std::vector<TagExpression>::const_iterator itEnd = m_tagExpressions.end(); - for(; it != itEnd; ++it ) - if( it->matches( testCase.getTags() ) ) - break; - if( it == itEnd ) - return false; - } - - if( !m_inclusionFilters.empty() ) { - std::vector<TestCaseFilter>::const_iterator it = m_inclusionFilters.begin(); - std::vector<TestCaseFilter>::const_iterator itEnd = m_inclusionFilters.end(); - for(; it != itEnd; ++it ) - if( it->shouldInclude( testCase ) ) - break; - if( it == itEnd ) - return false; - } - else if( m_exclusionFilters.empty() && m_tagExpressions.empty() ) { - return !testCase.isHidden(); - } - - std::vector<TestCaseFilter>::const_iterator it = m_exclusionFilters.begin(); - std::vector<TestCaseFilter>::const_iterator itEnd = m_exclusionFilters.end(); - for(; it != itEnd; ++it ) - if( !it->shouldInclude( testCase ) ) - return false; - return true; - } - private: - std::vector<TagExpression> m_tagExpressions; - std::vector<TestCaseFilter> m_inclusionFilters; - std::vector<TestCaseFilter> m_exclusionFilters; - std::string m_name; - }; + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); } @@ -1982,6 +1492,7 @@ namespace Catch { #include <iostream> #include <string> +#include <vector> namespace Catch { @@ -2002,6 +1513,8 @@ namespace Catch { Never }; }; + class TestCaseFilters; + struct IConfig : IShared { virtual ~IConfig(); @@ -2013,774 +1526,12 @@ namespace Catch { virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; + virtual std::vector<TestCaseFilters> const& filters() const = 0; }; } -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED - -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED - -#include <streambuf> - -namespace Catch { - - class StreamBufBase : public std::streambuf { - public: - virtual ~StreamBufBase() throw(); - }; -} - -#include <stdexcept> -#include <cstdio> - -namespace Catch { - - template<typename WriterF, size_t bufferSize=256> - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() throw() { - sync(); - } - - private: - int overflow( int c ) { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast<char>( c ) ) ); - else - sputc( static_cast<char>( c ) ); - } - return 0; - } - - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - class Stream { - public: - Stream() - : streamBuf( NULL ), isOwned( false ) - {} - - Stream( std::streambuf* _streamBuf, bool _isOwned ) - : streamBuf( _streamBuf ), isOwned( _isOwned ) - {} - - void release() { - if( isOwned ) { - delete streamBuf; - streamBuf = NULL; - isOwned = false; - } - } - - std::streambuf* streamBuf; - - private: - bool isOwned; - }; -} - -#include <memory> -#include <vector> -#include <string> -#include <iostream> - -#ifndef CATCH_CONFIG_CONSOLE_WIDTH -#define CATCH_CONFIG_CONSOLE_WIDTH 80 -#endif - -namespace Catch { - - struct ConfigData { - - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - abortAfter( -1 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ) - {} - - bool listTests; - bool listTags; - bool listReporters; - - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - - int abortAfter; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - - std::string reporterName; - std::string outputFilename; - std::string name; - std::string processName; - - std::vector<std::string> testsOrTags; - }; - - class Config : public SharedImpl<IConfig> { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); - public: - - Config() - : m_os( std::cout.rdbuf() ) - {} - - Config( ConfigData const& data ) - : m_data( data ), - m_os( std::cout.rdbuf() ) - { - if( !data.testsOrTags.empty() ) { - std::string groupName; - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) { - if( i != 0 ) - groupName += " "; - groupName += data.testsOrTags[i]; - } - TestCaseFilters filters( groupName ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) { - std::string filter = data.testsOrTags[i]; - if( startsWith( filter, "[" ) || startsWith( filter, "~[" ) ) - filters.addTags( filter ); - else - filters.addFilter( TestCaseFilter( filter ) ); - } - m_filterSets.push_back( filters ); - } - } - - virtual ~Config() { - m_os.rdbuf( std::cout.rdbuf() ); - m_stream.release(); - } - - void setFilename( std::string const& filename ) { - m_data.outputFilename = filename; - } - - std::string const& getFilename() const { - return m_data.outputFilename ; - } - - bool listTests() const { return m_data.listTests; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } - - std::string getProcessName() const { - return m_data.processName; - } - - bool shouldDebugBreak() const { - return m_data.shouldDebugBreak; - } - - void setStreamBuf( std::streambuf* buf ) { - m_os.rdbuf( buf ? buf : std::cout.rdbuf() ); - } - - void useStream( std::string const& streamName ) { - Stream stream = createStream( streamName ); - setStreamBuf( stream.streamBuf ); - m_stream.release(); - m_stream = stream; - } - - std::string getReporterName() const { return m_data.reporterName; } - - void addTestSpec( std::string const& testSpec ) { - TestCaseFilters filters( testSpec ); - filters.addFilter( TestCaseFilter( testSpec ) ); - m_filterSets.push_back( filters ); - } - - int abortAfter() const { - return m_data.abortAfter; - } - - std::vector<TestCaseFilters> const& filters() const { - return m_filterSets; - } - - bool showHelp() const { return m_data.showHelp; } - - // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_os; } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - - private: - ConfigData m_data; - - Stream m_stream; - mutable std::ostream m_os; - std::vector<TestCaseFilters> m_filterSets; - }; - -} // end namespace Catch - -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED - -namespace Catch { - - // An optional type - template<typename T> - class Option { - public: - Option() : nullableValue( NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) - {} - - ~Option() { - reset(); - } - - Option& operator= ( Option const& _other ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; - } - - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = NULL; - } - - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } - - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } - - bool some() const { return nullableValue != NULL; } - bool none() const { return nullableValue == NULL; } - - bool operator !() const { return nullableValue == NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); - } - - private: - T* nullableValue; - char storage[sizeof(T)]; - }; - -} // end namespace Catch - -#include <string> -#include <ostream> -#include <map> -#include <assert.h> - -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - - ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - - std::ostream& stream() const { return *m_stream; } - Ptr<IConfig> fullConfig() const { return m_fullConfig; } - - private: - std::ostream* m_stream; - Ptr<IConfig> m_fullConfig; - }; - - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; - }; - - template<typename T> - struct LazyStat : Option<T> { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option<T>::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option<T>::reset(); - used = false; - } - bool used; - }; - - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; - - struct SectionInfo { - SectionInfo( std::string const& _name, - std::string const& _description, - SourceLineInfo const& _lineInfo ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - std::string name; - std::string description; - SourceLineInfo lineInfo; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector<MessageInfo> const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - - AssertionResult assertionResult; - std::vector<MessageInfo> infoMessages; - Totals totals; - }; - - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; - - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); - - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; - - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); - - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; - - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} - virtual ~TestRunStats(); - - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; - - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); - - // Implementing class must also provide the following static method: - // static std::string getDescription(); - - virtual ReporterPreferences getPreferences() const = 0; - - virtual void noMatchingTestCases( std::string const& spec ) = 0; - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - }; - - struct StreamingReporterBase : SharedImpl<IStreamingReporter> { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - {} - - virtual ~StreamingReporterBase(); - - virtual void noMatchingTestCases( std::string const& ) {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { - currentTestCaseInfo.reset(); - assert( m_sectionStack.empty() ); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - Ptr<IConfig> m_config; - std::ostream& stream; - - LazyStat<TestRunInfo> currentTestRunInfo; - LazyStat<GroupInfo> currentGroupInfo; - LazyStat<TestCaseInfo> currentTestCaseInfo; - - std::vector<SectionInfo> m_sectionStack; - }; - - struct CumulativeReporterBase : SharedImpl<IStreamingReporter> { - template<typename T, typename ChildNodeT> - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - typedef std::vector<Ptr<ChildNodeT> > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); - - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == ( Ptr<SectionNode> const& other ) const { - return operator==( *other ); - } - - SectionStats stats; - typedef std::vector<Ptr<SectionNode> > ChildSections; - typedef std::vector<AssertionStats> Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - friend bool operator == ( Ptr<SectionNode> const& node, SectionInfo const& other ) { - return node->stats.sectionInfo.lineInfo == other.lineInfo; - } - - typedef Node<TestCaseStats, SectionNode> TestCaseNode; - typedef Node<TestGroupStats, TestCaseNode> TestGroupNode; - typedef Node<TestRunStats, TestGroupNode> TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - {} - ~CumulativeReporterBase(); - - virtual void testRunStarting( TestRunInfo const& ) {} - virtual void testGroupStarting( GroupInfo const& ) {} - - virtual void testCaseStarting( TestCaseInfo const& ) {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr<SectionNode> node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find( parentNode.childSections.begin(), parentNode.childSections.end(), sectionInfo ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = node; - } - - virtual void assertionStarting( AssertionInfo const& ) {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { - Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); - - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { - Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) { - Ptr<TestRunNode> node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEnded(); - } - virtual void testRunEnded() = 0; - - Ptr<IConfig> m_config; - std::ostream& stream; - std::vector<AssertionStats> m_assertions; - std::vector<std::vector<Ptr<SectionNode> > > m_sections; - std::vector<Ptr<TestCaseNode> > m_testCases; - std::vector<Ptr<TestGroupNode> > m_testGroups; - - std::vector<Ptr<TestRunNode> > m_testRuns; - - Ptr<SectionNode> m_rootSection; - Ptr<SectionNode> m_deepestSection; - std::vector<Ptr<SectionNode> > m_sectionStack; - - }; - - // Deprecated - struct IReporter : IShared { - virtual ~IReporter(); - - virtual bool shouldRedirectStdout() const = 0; - - virtual void StartTesting() = 0; - virtual void EndTesting( Totals const& totals ) = 0; - virtual void StartGroup( std::string const& groupName ) = 0; - virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; - virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; - virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; - virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; - virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; - virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; - virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; - virtual void Aborted() = 0; - virtual void Result( AssertionResult const& result ) = 0; - }; - - struct IReporterFactory { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map<std::string, IReporterFactory*> FactoryMap; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - }; - - inline std::string trim( std::string const& str ) { - static char const* whitespaceChars = "\n\r\t "; - 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 ) : ""; - } -} - -#include <vector> - -namespace Catch { - - class TestCase; - struct ITestCaseRegistry; - struct IExceptionTranslatorRegistry; - struct IExceptionTranslator; - - struct IRegistryHub { - virtual ~IRegistryHub(); - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; - }; - - struct IMutableRegistryHub { - virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; - virtual void registerTest( TestCase const& testInfo ) = 0; - virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; - }; - - IRegistryHub& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - #include <ostream> namespace Catch { @@ -2823,32 +1574,25 @@ struct TestFailureException{}; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ASSERTIONINFO_NAME INTERNAL_CATCH_UNIQUE_NAME( __assertionInfo ) - -/////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ACCEPT_EXPR( evaluatedExpr, resultDisposition, originalExpr ) \ - if( Catch::ResultAction::Value internal_catch_action = Catch::getResultCapture().acceptExpression( evaluatedExpr, INTERNAL_CATCH_ASSERTIONINFO_NAME ) ) { \ - if( internal_catch_action & Catch::ResultAction::Debug ) BreakIntoDebugger(); \ + if( Catch::ResultAction::Value internal_catch_action = Catch::getResultCapture().acceptExpression( evaluatedExpr, __assertionInfo ) ) { \ + if( internal_catch_action & Catch::ResultAction::Debug ) CATCH_BREAK_INTO_DEBUGGER(); \ if( internal_catch_action & Catch::ResultAction::Abort ) throw Catch::TestFailureException(); \ if( !Catch::shouldContinueOnFailure( resultDisposition ) ) throw Catch::TestFailureException(); \ Catch::isTrue( false && originalExpr ); \ } /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ACCEPT_INFO( expr, macroName, resultDisposition ) \ - Catch::AssertionInfo INTERNAL_CATCH_ASSERTIONINFO_NAME( macroName, CATCH_INTERNAL_LINEINFO, expr, resultDisposition ); - -/////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ do { \ - INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionDecomposer()->*expr ).endExpression( resultDisposition ), resultDisposition, expr ); \ } catch( Catch::TestFailureException& ) { \ throw; \ } catch( ... ) { \ INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException(), \ - resultDisposition | Catch::ResultDisposition::ContinueOnFailure, expr ); \ + Catch::ResultDisposition::Normal, expr ); \ } \ } while( Catch::isTrue( false ) ) @@ -2865,7 +1609,7 @@ struct TestFailureException{}; /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ do { \ - INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ try { \ expr; \ INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::Ok ), resultDisposition, false ); \ @@ -2893,14 +1637,14 @@ struct TestFailureException{}; /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( expr, exceptionType, resultDisposition, macroName ) \ do { \ - INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ } while( Catch::isTrue( false ) ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ do { \ - INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ catch( ... ) { \ INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException() ), \ @@ -2909,11 +1653,19 @@ struct TestFailureException{}; } while( Catch::isTrue( false ) ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_MSG( log, messageType, resultDisposition, macroName ) \ - do { \ - INTERNAL_CATCH_ACCEPT_INFO( "", macroName, resultDisposition ); \ - INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << log, resultDisposition, true ) \ - } while( Catch::isTrue( false ) ) +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + do { \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << __VA_ARGS__ +::Catch::StreamEndStop(), resultDisposition, true ) \ + } while( Catch::isTrue( false ) ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << log, resultDisposition, true ) \ + } while( Catch::isTrue( false ) ) +#endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( log, macroName ) \ @@ -2922,7 +1674,7 @@ struct TestFailureException{}; /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ do { \ - INTERNAL_CATCH_ACCEPT_INFO( #arg " " #matcher, macroName, resultDisposition ); \ + Catch::AssertionInfo __assertionInfo( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ try { \ INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::expressionResultBuilderFromMatcher( ::Catch::Matchers::matcher, arg, #matcher ) ), resultDisposition, false ); \ } catch( Catch::TestFailureException& ) { \ @@ -2933,8 +1685,88 @@ struct TestFailureException{}; } \ } while( Catch::isTrue( false ) ) -// #included from: internal/catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +namespace Catch { + + struct SectionInfo { + SectionInfo( std::string const& _name, + std::string const& _description, + SourceLineInfo const& _lineInfo ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include <cstddef> + +namespace Catch { + + struct Counts { + Counts() : passed( 0 ), failed( 0 ) {} + + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + return *this; + } + + std::size_t total() const { + return passed + failed; + } + + std::size_t passed; + std::size_t failed; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; + }; +} // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED @@ -2969,24 +1801,20 @@ namespace Catch { public: Section( SourceLineInfo const& lineInfo, std::string const& name, - std::string const& description = "" ) - : m_info( name, description, lineInfo ), - m_sectionIncluded( getCurrentContext().getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); - } - - ~Section() { - if( m_sectionIncluded ) - getCurrentContext().getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); - } + std::string const& description = "" ); + ~Section(); +# ifdef CATCH_CPP11_OR_GREATER + Section( Section const& ) = default; + Section( Section && ) = default; + Section& operator = ( Section const& ) = default; + Section& operator = ( Section && ) = default; +# endif // This indicates whether the section should be executed or not - operator bool() { - return m_sectionIncluded; - } + operator bool(); private: + SectionInfo m_info; std::string m_name; @@ -3305,7 +2133,7 @@ namespace Detail { std::string toString() const { std::ostringstream oss; - oss << "Approx( " << m_value << " )"; + oss << "Approx( " << Catch::toString( m_value ) << " )"; return oss.str(); } @@ -3550,10 +2378,81 @@ using namespace Matchers; // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections -// #included from: internal/catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #include <string> +#include <set> + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestCase; + + struct TestCaseInfo { + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set<std::string> const& _tags, + bool _isHidden, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + std::string name; + std::string className; + std::string description; + std::set<std::string> tags; + std::string tagsAsString; + SourceLineInfo lineInfo; + bool isHidden; + bool throws; + }; + + class TestCase : protected TestCaseInfo { + public: + + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool isHidden() const; + bool throws() const; + bool hasTag( std::string const& tag ) const; + bool matchesTags( std::string const& tagPattern ) const; + std::set<std::string> const& getTags() const; + + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); + + private: + Ptr<ITestCase> test; + }; + + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: internal/catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED namespace Catch { class TestCase; @@ -3613,10 +2512,6 @@ namespace Catch { namespace Detail{ - inline bool startsWith( std::string const& str, std::string const& sub ) { - return str.length() > sub.length() && str.substr( 0, sub.length() ) == sub; - } - inline std::string getAnnotation( Class cls, std::string const& annotationName, std::string const& testCaseName ) { @@ -3645,7 +2540,7 @@ namespace Catch { for( u_int m = 0; m < count ; m++ ) { SEL selector = method_getName(methods[m]); std::string methodName = sel_getName(selector); - if( Detail::startsWith( methodName, "Catch_TestCase_" ) ) { + if( startsWith( methodName, "Catch_TestCase_" ) ) { std::string testCaseName = methodName.substr( 15 ); std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); @@ -3762,7 +2657,7 @@ return @ desc; \ #endif -#if defined( CATCH_CONFIG_MAIN ) || defined( CATCH_CONFIG_RUNNER ) +#ifdef CATCH_CONFIG_RUNNER // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED @@ -3780,22 +2675,405 @@ return @ desc; \ // #included from: internal/catch_commandline.hpp #define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED -// #included from: clara.h -#define TWOBLUECUBES_CLARA_H_INCLUDED +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED -// #included from: catch_text.h -#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED +// #included from: catch_test_spec.h +#define TWOBLUECUBES_CATCH_TEST_SPEC_H_INCLUDED + +// #included from: catch_tags.h +#define TWOBLUECUBES_CATCH_TAGS_H_INCLUDED + +#include <string> +#include <set> +#include <map> +#include <vector> + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + class TagParser { + public: + virtual ~TagParser(); + + void parse( std::string const& str ); + + protected: + virtual void acceptTag( std::string const& tag ) = 0; + virtual void acceptChar( char c ) = 0; + virtual void endParse() {} + + private: + }; + + class TagExtracter : public TagParser { + public: + + TagExtracter( std::set<std::string>& tags ); + virtual ~TagExtracter(); + + void parse( std::string& description ); + + private: + virtual void acceptTag( std::string const& tag ); + virtual void acceptChar( char c ); + + TagExtracter& operator=(TagExtracter const&); + + std::set<std::string>& m_tags; + std::string m_remainder; + }; + + class Tag { + public: + Tag(); + Tag( std::string const& name, bool isNegated ); + std::string getName() const; + bool isNegated() const; + bool operator ! () const; + + private: + std::string m_name; + bool m_isNegated; + }; + + class TagSet { + typedef std::map<std::string, Tag> TagMap; + public: + void add( Tag const& tag ); + bool empty() const; + bool matches( std::set<std::string> const& tags ) const; + + private: + TagMap m_tags; + }; + + class TagExpression { + public: + bool matches( std::set<std::string> const& tags ) const; + + private: + friend class TagExpressionParser; + + std::vector<TagSet> m_tagSets; + }; + + class TagExpressionParser : public TagParser { + public: + TagExpressionParser( TagExpression& exp ); + ~TagExpressionParser(); + + private: + virtual void acceptTag( std::string const& tag ); + virtual void acceptChar( char c ); + virtual void endParse(); + + TagExpressionParser& operator=(TagExpressionParser const&); + + bool m_isNegated; + TagSet m_currentTagSet; + TagExpression& m_exp; + }; + +} // end namespace Catch #include <string> #include <vector> namespace Catch { + class TestCase; + + struct IfFilterMatches{ enum DoWhat { + AutoDetectBehaviour, + IncludeTests, + ExcludeTests + }; }; + + class TestCaseFilter { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + TestCaseFilter( std::string const& testSpec, IfFilterMatches::DoWhat matchBehaviour = IfFilterMatches::AutoDetectBehaviour ); + + IfFilterMatches::DoWhat getFilterType() const; + bool shouldInclude( TestCase const& testCase ) const; + + private: + bool isMatch( TestCase const& testCase ) const; + + std::string m_stringToMatch; + IfFilterMatches::DoWhat m_filterType; + WildcardPosition m_wildcardPosition; + }; + + class TestCaseFilters { + public: + TestCaseFilters( std::string const& name ); + std::string getName() const; + void addFilter( TestCaseFilter const& filter ); + void addTags( std::string const& tagPattern ); + bool shouldInclude( TestCase const& testCase ) const; + + private: + std::vector<TagExpression> m_tagExpressions; + std::vector<TestCaseFilter> m_inclusionFilters; + std::vector<TestCaseFilter> m_exclusionFilters; + std::string m_name; + }; + +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +#include <streambuf> + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + class Stream { + public: + Stream(); + Stream( std::streambuf* _streamBuf, bool _isOwned ); + void release(); + + std::streambuf* streamBuf; + + private: + bool isOwned; + }; +} + +#include <memory> +#include <vector> +#include <string> +#include <iostream> + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct ConfigData { + + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + showInvisibles( false ), + abortAfter( -1 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ) + {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + + int abortAfter; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + + std::string reporterName; + std::string outputFilename; + std::string name; + std::string processName; + + std::vector<std::string> testsOrTags; + }; + + class Config : public SharedImpl<IConfig> { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: + + Config() + : m_os( std::cout.rdbuf() ) + {} + + Config( ConfigData const& data ) + : m_data( data ), + m_os( std::cout.rdbuf() ) + { + if( !data.testsOrTags.empty() ) { + std::string groupName; + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) { + if( i != 0 ) + groupName += " "; + groupName += data.testsOrTags[i]; + } + TestCaseFilters filters( groupName ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) { + std::string filter = data.testsOrTags[i]; + if( startsWith( filter, "[" ) || startsWith( filter, "~[" ) ) + filters.addTags( filter ); + else + filters.addFilter( TestCaseFilter( filter ) ); + } + m_filterSets.push_back( filters ); + } + } + + virtual ~Config() { + m_os.rdbuf( std::cout.rdbuf() ); + m_stream.release(); + } + + void setFilename( std::string const& filename ) { + m_data.outputFilename = filename; + } + + std::string const& getFilename() const { + return m_data.outputFilename ; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { + return m_data.processName; + } + + bool shouldDebugBreak() const { + return m_data.shouldDebugBreak; + } + + void setStreamBuf( std::streambuf* buf ) { + m_os.rdbuf( buf ? buf : std::cout.rdbuf() ); + } + + void useStream( std::string const& streamName ) { + Stream stream = createStream( streamName ); + setStreamBuf( stream.streamBuf ); + m_stream.release(); + m_stream = stream; + } + + std::string getReporterName() const { return m_data.reporterName; } + + void addTestSpec( std::string const& testSpec ) { + TestCaseFilters filters( testSpec ); + filters.addFilter( TestCaseFilter( testSpec ) ); + m_filterSets.push_back( filters ); + } + + int abortAfter() const { + return m_data.abortAfter; + } + + std::vector<TestCaseFilters> const& filters() const { + return m_filterSets; + } + + bool showHelp() const { return m_data.showHelp; } + bool showInvisibles() const { return m_data.showInvisibles; } + + // IConfig interface + virtual bool allowThrows() const { return !m_data.noThrow; } + virtual std::ostream& stream() const { return m_os; } + virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } + + private: + ConfigData m_data; + + Stream m_stream; + mutable std::ostream m_os; + std::vector<TestCaseFilters> m_filterSets; + }; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: ../external/clara.h + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include <string> +#include <vector> +#include <sstream> + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + struct TextAttributes { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), - width( CATCH_CONFIG_CONSOLE_WIDTH-1 ), + width( consoleWidth-1 ), tabChar( '\t' ) {} @@ -3812,8 +3090,66 @@ namespace Catch { class Text { public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ); - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ); + 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; + + 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 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + 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 ); + } typedef std::vector<std::string>::const_iterator const_iterator; @@ -3822,9 +3158,21 @@ namespace Catch { std::string const& last() const { return lines.back(); } std::size_t size() const { return lines.size(); } std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const; + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } - friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ); + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } private: std::string str; @@ -3832,10 +3180,53 @@ namespace Catch { std::vector<std::string> lines; }; -} // end namespace Catch +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +#include <map> +#include <algorithm> +#include <stdexcept> +#include <memory> + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif namespace Clara { + + struct UnpositionalTag {}; + + extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN + UnpositionalTag _; +#endif + namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + using namespace Tbc; + + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } + template<typename T> struct RemoveConstRef{ typedef T type; }; template<typename T> struct RemoveConstRef<T&>{ typedef T type; }; template<typename T> struct RemoveConstRef<T const&>{ typedef T type; }; @@ -3876,6 +3267,10 @@ namespace Clara { template<typename ConfigT> struct IArgFunction { virtual ~IArgFunction() {} +# ifdef CATCH_CPP11_OR_GREATER + IArgFunction() = default; + IArgFunction( IArgFunction const& ) = default; +# endif virtual void set( ConfigT& config, std::string const& value ) const = 0; virtual void setFlag( ConfigT& config ) const = 0; virtual bool takesArg() const = 0; @@ -3885,10 +3280,11 @@ namespace Clara { template<typename ConfigT> class BoundArgFunction { public: + BoundArgFunction() : functionObj( NULL ) {} BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj->clone() ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction<ConfigT>* newFunctionObj = other.functionObj->clone(); + IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; delete functionObj; functionObj = newFunctionObj; return *this; @@ -3902,6 +3298,10 @@ namespace Clara { functionObj->setFlag( config ); } bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != NULL; + } private: IArgFunction<ConfigT>* functionObj; }; @@ -3996,26 +3396,6 @@ namespace Clara { void (*function)( C&, T ); }; - template<typename C, typename M> - BoundArgFunction<C> makeBoundField( M C::* _member ) { - return BoundArgFunction<C>( new BoundDataMember<C,M>( _member ) ); - } - template<typename C, typename M> - BoundArgFunction<C> makeBoundField( void (C::*_member)( M ) ) { - return BoundArgFunction<C>( new BoundUnaryMethod<C,M>( _member ) ); - } - template<typename C> - BoundArgFunction<C> makeBoundField( void (C::*_member)() ) { - return BoundArgFunction<C>( new BoundNullaryMethod<C>( _member ) ); - } - template<typename C> - BoundArgFunction<C> makeBoundField( void (*_function)( C& ) ) { - return BoundArgFunction<C>( new BoundUnaryFunction<C>( _function ) ); - } - template<typename C, typename T> - BoundArgFunction<C> makeBoundField( void (*_function)( C&, T ) ) { - return BoundArgFunction<C>( new BoundBinaryFunction<C, T>( _function ) ); - } } // namespace Detail struct Parser { @@ -4029,7 +3409,8 @@ namespace Clara { }; void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const { - for( int i = 1; i < argc; ++i ) + const std::string doubleDash = "--"; + for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) parseIntoTokens( argv[i] , tokens); } void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const { @@ -4062,32 +3443,52 @@ namespace Clara { }; template<typename ConfigT> + struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {} + + Detail::BoundArgFunction<ConfigT> boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if( !boundField.isSet() ) + throw std::logic_error( "option not bound" ); + } + }; + struct OptionArgProperties { + std::vector<std::string> shortNames; + std::string longName; + + bool hasShortName( std::string const& shortName ) const { + return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + }; + struct PositionalArgProperties { + PositionalArgProperties() : position( -1 ) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } + }; + + template<typename ConfigT> class CommandLine { - struct Arg { - Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ), position( -1 ) {} + struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {} + + using CommonArgProperties<ConfigT>::placeholder; // !TBD - bool hasShortName( std::string const& shortName ) const { - for( std::vector<std::string>::const_iterator - it = shortNames.begin(), itEnd = shortNames.end(); - it != itEnd; - ++it ) - if( *it == shortName ) - return true; - return false; - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - bool takesArg() const { - return !hint.empty(); - } - bool isFixedPositional() const { - return position != -1; - } - bool isAnyPositional() const { - return position == -1 && shortNames.empty() && longName.empty(); - } std::string dbgName() const { if( !longName.empty() ) return "--" + longName; @@ -4095,10 +3496,6 @@ namespace Clara { return "-" + shortNames[0]; return "positional args"; } - void validate() const { - if( boundField.takesArg() && !takesArg() ) - throw std::logic_error( dbgName() + " must specify an arg name" ); - } std::string commands() const { std::ostringstream oss; bool first = true; @@ -4115,113 +3512,182 @@ namespace Clara { oss << ", "; oss << "--" << longName; } - if( !hint.empty() ) - oss << " <" << hint << ">"; + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; return oss.str(); } - - Detail::BoundArgFunction<ConfigT> boundField; - std::vector<std::string> shortNames; - std::string longName; - std::string description; - std::string hint; - int position; }; - class ArgBinder { + // NOTE: std::auto_ptr is deprecated in c++11/c++0x +#if defined(__cplusplus) && __cplusplus > 199711L + typedef std::unique_ptr<Arg> ArgAutoPtr; +#else + typedef std::auto_ptr<Arg> ArgAutoPtr; +#endif + + friend void addOptName( Arg& arg, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( Arg& arg, int position ) + { + arg.position = position; + } + + class ArgBuilder { public: - template<typename F> - ArgBinder( CommandLine* cl, F f ) - : m_cl( cl ), - m_arg( Detail::makeBoundField( f ) ) - {} - ArgBinder( ArgBinder& other ) - : m_cl( other.m_cl ), - m_arg( other.m_arg ) - { - other.m_cl = NULL; - } - ~ArgBinder() { - if( m_cl ) { - m_arg.validate(); - if( m_arg.isFixedPositional() ) { - m_cl->m_positionalArgs.insert( std::make_pair( m_arg.position, m_arg ) ); - if( m_arg.position > m_cl->m_highestSpecifiedArgPosition ) - m_cl->m_highestSpecifiedArgPosition = m_arg.position; - } - else if( m_arg.isAnyPositional() ) { - if( m_cl->m_arg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_cl->m_arg = std::auto_ptr<Arg>( new Arg( m_arg ) ); - } - else - m_cl->m_options.push_back( m_arg ); - } + ArgBuilder( Arg* arg ) : m_arg( arg ) {} + + // Bind a non-boolean data member (requires placeholder string) + template<typename C, typename M> + void bind( M C::* field, std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundDataMember<C,M>( field ); + m_arg->placeholder = placeholder; } - ArgBinder& shortOpt( std::string const& name ) { - m_arg.shortNames.push_back( name ); - return *this; + // Bind a boolean data member (no placeholder required) + template<typename C> + void bind( bool C::* field ) { + m_arg->boundField = new Detail::BoundDataMember<C,bool>( field ); } - ArgBinder& longOpt( std::string const& name ) { - m_arg.longName = name; - return *this; + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template<typename C, typename M> + void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod ); + m_arg->placeholder = placeholder; } - ArgBinder& describe( std::string const& description ) { - m_arg.description = description; + + // Bind a method taking a single, boolean argument (no placeholder string required) + template<typename C> + void bind( void (C::* unaryMethod)( bool ) ) { + m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod ); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template<typename C> + void bind( void (C::* nullaryMethod)() ) { + m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod ); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template<typename C> + void bind( void (* unaryFunction)( C& ) ) { + m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction ); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template<typename C, typename T> + void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction ); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg->description = description; return *this; } - ArgBinder& hint( std::string const& hint ) { - m_arg.hint = hint; + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; return *this; } - ArgBinder& position( int position ) { - m_arg.position = position; + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *ArgBuilder::m_arg, optName ); return *this; } - private: - CommandLine* m_cl; - Arg m_arg; }; public: CommandLine() : m_boundProcessName( new Detail::NullBinder<ConfigT>() ), - m_highestSpecifiedArgPosition( 0 ) + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) {} CommandLine( CommandLine const& other ) : m_boundProcessName( other.m_boundProcessName ), m_options ( other.m_options ), m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) { - if( other.m_arg.get() ) - m_arg = std::auto_ptr<Arg>( new Arg( *other.m_arg ) ); + if( other.m_floatingArg.get() ) + m_floatingArg = ArgAutoPtr( new Arg( *other.m_floatingArg ) ); } - template<typename F> - ArgBinder bind( F f ) { - ArgBinder binder( this, f ); - return binder; + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; } - template<typename F> - void bindProcessName( F f ) { - m_boundProcessName = Detail::makeBoundField( f ); + + OptBuilder operator[]( std::string const& optName ) { + m_options.push_back( Arg() ); + addOptName( m_options.back(), optName ); + OptBuilder builder( &m_options.back() ); + return builder; + } + + ArgBuilder operator[]( int position ) { + m_positionalArgs.insert( std::make_pair( position, Arg() ) ); + if( position > m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition = position; + setPositionalArg( m_positionalArgs[position], position ); + ArgBuilder builder( &m_positionalArgs[position] ); + return builder; } - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = CATCH_CONFIG_CONSOLE_WIDTH ) const { + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + if( m_floatingArg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_floatingArg = ArgAutoPtr( new Arg() ); + ArgBuilder builder( m_floatingArg.get() ); + return builder; + } + + template<typename C, typename M> + void bindProcessName( M C::* field ) { + m_boundProcessName = new Detail::BoundDataMember<C,M>( field ); + } + template<typename C, typename M> + void bindProcessName( void (C::*_unaryMethod)( M ) ) { + m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; std::size_t maxWidth = 0; for( it = itBegin; it != itEnd; ++it ) maxWidth = (std::max)( maxWidth, it->commands().size() ); for( it = itBegin; it != itEnd; ++it ) { - Catch::Text usage( it->commands(), Catch::TextAttributes() + Detail::Text usage( it->commands(), Detail::TextAttributes() .setWidth( maxWidth+indent ) .setIndent( indent ) ); - // !TBD handle longer usage strings - Catch::Text desc( it->description, Catch::TextAttributes() - .setWidth( width - maxWidth -3 ) ); + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth - 3 ) ); for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { std::string usageCol = i < usage.size() ? usage[i] : ""; @@ -4246,17 +3712,17 @@ namespace Clara { os << " "; typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i ); if( it != m_positionalArgs.end() ) - os << "<" << it->second.hint << ">"; - else if( m_arg.get() ) - os << "<" << m_arg->hint << ">"; + os << "<" << it->second.placeholder << ">"; + else if( m_floatingArg.get() ) + os << "<" << m_floatingArg->placeholder << ">"; else throw std::logic_error( "non consecutive positional arguments with no floating args" ); } // !TBD No indication of mandatory args - if( m_arg.get() ) { + if( m_floatingArg.get() ) { if( m_highestSpecifiedArgPosition > 1 ) os << " "; - os << "[<" << m_arg->hint << "> ...]"; + os << "[<" << m_floatingArg->placeholder << "> ...]"; } } std::string argSynopsis() const { @@ -4266,6 +3732,7 @@ namespace Clara { } void usage( std::ostream& os, std::string const& procName ) const { + validate(); os << "usage:\n " << procName << " "; argSynopsis( os ); if( !m_options.empty() ) { @@ -4280,6 +3747,12 @@ namespace Clara { return oss.str(); } + ConfigT parse( int argc, char const * const * argv ) const { + ConfigT config; + parseInto( argc, argv, config ); + return config; + } + std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const { std::string processName = argv[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); @@ -4293,9 +3766,7 @@ namespace Clara { } std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { - if( m_options.empty() && m_positionalArgs.empty() ) - throw std::logic_error( "No options or arguments specified" ); - + validate(); std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config ); unusedTokens = populateFixedArgs( unusedTokens, config ); unusedTokens = populateFloatingArgs( unusedTokens, config ); @@ -4304,6 +3775,7 @@ namespace Clara { std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { std::vector<Parser::Token> unusedTokens; + std::vector<std::string> errors; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end(); @@ -4315,8 +3787,9 @@ namespace Clara { ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { if( arg.takesArg() ) { if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - throw std::domain_error( "Expected argument to option " + token.data ); - arg.boundField.set( config, tokens[++i].data ); + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); } else { arg.boundField.setFlag( config ); @@ -4325,11 +3798,26 @@ namespace Clara { } } catch( std::exception& ex ) { - throw std::runtime_error( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); } } - if( it == itEnd ) - unusedTokens.push_back( token ); + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); + } + } + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); } return unusedTokens; } @@ -4349,29 +3837,56 @@ namespace Clara { return unusedTokens; } std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { - if( !m_arg.get() ) + if( !m_floatingArg.get() ) return tokens; std::vector<Parser::Token> unusedTokens; for( std::size_t i = 0; i < tokens.size(); ++i ) { Parser::Token const& token = tokens[i]; if( token.type == Parser::Token::Positional ) - m_arg->boundField.set( config, token.data ); + m_floatingArg->boundField.set( config, token.data ); else unusedTokens.push_back( token ); } return unusedTokens; } + void validate() const + { + if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) + throw std::logic_error( "No options or arguments specified" ); + + for( typename std::vector<Arg>::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it ) + it->validate(); + } + private: Detail::BoundArgFunction<ConfigT> m_boundProcessName; std::vector<Arg> m_options; std::map<int, Arg> m_positionalArgs; - std::auto_ptr<Arg> m_arg; + ArgAutoPtr m_floatingArg; int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; }; } // end namespace Clara +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include <fstream> + namespace Catch { inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } @@ -4398,99 +3913,106 @@ namespace Catch { ? ShowDurations::Always : ShowDurations::Never; } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, "#" ) ) + addTestOrTags( config, line ); + } + } inline Clara::CommandLine<ConfigData> makeCommandLineParser() { - Clara::CommandLine<ConfigData> cli; + using namespace Clara; + CommandLine<ConfigData> cli; cli.bindProcessName( &ConfigData::processName ); - cli.bind( &ConfigData::showHelp ) + cli["-?"]["-h"]["--help"] .describe( "display usage information" ) - .shortOpt( "?") - .shortOpt( "h") - .longOpt( "help" ); - - cli.bind( &ConfigData::listTests ) - .describe( "list all (or matching) test cases" ) - .shortOpt( "l") - .longOpt( "list-tests" ); + .bind( &ConfigData::showHelp ); - cli.bind( &ConfigData::listTags ) - .describe( "list all (or matching) tags" ) - .shortOpt( "t") - .longOpt( "list-tags" ); + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .bind( &ConfigData::listTests ); - cli.bind( &ConfigData::listReporters ) - .describe( "list all reporters" ) - .longOpt( "list-reporters" ); + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .bind( &ConfigData::listTags ); - cli.bind( &ConfigData::showSuccessfulTests ) + cli["-s"]["--success"] .describe( "include successful tests in output" ) - .shortOpt( "s") - .longOpt( "success" ); + .bind( &ConfigData::showSuccessfulTests ); - cli.bind( &ConfigData::shouldDebugBreak ) + cli["-b"]["--break"] .describe( "break into debugger on failure" ) - .shortOpt( "b") - .longOpt( "break" ); + .bind( &ConfigData::shouldDebugBreak ); - cli.bind( &ConfigData::noThrow ) + cli["-e"]["--nothrow"] .describe( "skip exception tests" ) - .shortOpt( "e") - .longOpt( "nothrow" ); + .bind( &ConfigData::noThrow ); - cli.bind( &ConfigData::outputFilename ) + cli["-i"]["--invisibles"] + .describe( "show invisibles (tabs, newlines)" ) + .bind( &ConfigData::showInvisibles ); + + cli["-o"]["--out"] .describe( "output filename" ) - .shortOpt( "o") - .longOpt( "out" ) - .hint( "filename" ); - - cli.bind( &ConfigData::reporterName ) - .describe( "reporter to use - defaults to console" ) - .shortOpt( "r") - .longOpt( "reporter" ) -// .hint( "name[:filename]" ); - .hint( "name" ); - - cli.bind( &ConfigData::name ) + .bind( &ConfigData::outputFilename, "filename" ); + + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .describe( "reporter to use (defaults to console)" ) + .bind( &ConfigData::reporterName, "name" ); + + cli["-n"]["--name"] .describe( "suite name" ) - .shortOpt( "n") - .longOpt( "name" ) - .hint( "name" ); + .bind( &ConfigData::name, "name" ); - cli.bind( &abortAfterFirst ) + cli["-a"]["--abort"] .describe( "abort at first failure" ) - .shortOpt( "a") - .longOpt( "abort" ); + .bind( &abortAfterFirst ); - cli.bind( &abortAfterX ) + cli["-x"]["--abortx"] .describe( "abort after x failures" ) - .shortOpt( "x") - .longOpt( "abortx" ) - .hint( "number of failures" ); + .bind( &abortAfterX, "no. failures" ); - cli.bind( &addWarning ) + cli["-w"]["--warn"] .describe( "enable warnings" ) - .shortOpt( "w") - .longOpt( "warn" ) - .hint( "warning name" ); + .bind( &addWarning, "warning name" ); -// cli.bind( &setVerbosity ) +// - needs updating if reinstated +// cli.into( &setVerbosity ) // .describe( "level of verbosity (0=no output)" ) // .shortOpt( "v") // .longOpt( "verbosity" ) -// .hint( "level" ); +// .placeholder( "level" ); - cli.bind( &addTestOrTags ) + cli[_] .describe( "which test or tests to use" ) - .hint( "test name, pattern or tags" ); + .bind( &addTestOrTags, "test name, pattern or tags" ); - cli.bind( &setShowDurations ) + cli["-d"]["--durations"] .describe( "show test durations" ) - .shortOpt( "d") - .longOpt( "durations" ) - .hint( "yes/no" ); + .bind( &setShowDurations, "yes/no" ); + + cli["-f"]["--input-file"] + .describe( "load test names to run from a file" ) + .bind( &loadTestNamesFromFile, "filename" ); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .bind( &ConfigData::listTestNamesOnly ); + + cli["--list-reporters"] + .describe( "list all reporters" ) + .bind( &ConfigData::listReporters ); return cli; } @@ -4500,6 +4022,166 @@ namespace Catch { // #included from: internal/catch_list.hpp #define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include <string> +#include <vector> +#include <sstream> + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + 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 { + public: + 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; + + 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 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + 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 ); + } + + typedef std::vector<std::string>::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector<std::string> lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { + using Tbc::Text; + using Tbc::TextAttributes; +} + // #included from: catch_console_colour.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED @@ -4551,23 +4233,325 @@ namespace Catch { static void use( Code _colourCode ); private: - static Detail::IColourImpl* impl; + static Detail::IColourImpl* impl(); }; } // end namespace Catch +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + + // An optional type + template<typename T> + class Option { + public: + Option() : nullableValue( NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != NULL; } + bool none() const { return nullableValue == NULL; } + + bool operator !() const { return nullableValue == NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; + +} // end namespace Catch + +#include <string> +#include <ostream> +#include <map> +#include <assert.h> + +namespace Catch +{ + struct ReporterConfig { + explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& stream() const { return *m_stream; } + Ptr<IConfig> fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr<IConfig> m_fullConfig; + }; + + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} + + bool shouldRedirectStdOut; + }; + + template<typename T> + struct LazyStat : Option<T> { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option<T>::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option<T>::reset(); + used = false; + } + bool used; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); + +# ifdef CATCH_CPP11_OR_GREATER + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; +# endif + + AssertionResult assertionResult; + std::vector<MessageInfo> infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); +# ifdef CATCH_CPP11_OR_GREATER + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; +# endif + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); + +# ifdef CATCH_CPP11_OR_GREATER + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; +# endif + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); + +# ifdef CATCH_CPP11_OR_GREATER + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; +# endif + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + virtual ~TestRunStats(); + +# ifndef CATCH_CPP11_OR_GREATER + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} +# else + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; +# endif + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + }; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map<std::string, IReporterFactory*> FactoryMap; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + }; + +} + #include <limits> #include <algorithm> namespace Catch { - inline bool matchesFilters( std::vector<TestCaseFilters> const& filters, TestCase const& testCase ) { - std::vector<TestCaseFilters>::const_iterator it = filters.begin(); - std::vector<TestCaseFilters>::const_iterator itEnd = filters.end(); - for(; it != itEnd; ++it ) - if( !it->shouldInclude( testCase ) ) - return false; - return true; - } inline std::size_t listTests( Config const& config ) { if( config.filters().empty() ) @@ -4580,22 +4564,22 @@ namespace Catch { nameAttr.setInitialIndent( 2 ).setIndent( 4 ); tagsAttr.setIndent( 6 ); - std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); - for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); + std::vector<TestCase> matchedTestCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases ); + for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; - ++it ) - if( matchesFilters( config.filters(), *it ) ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); - std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( !testCaseInfo.tags.empty() ) - std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } + std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } if( config.filters().empty() ) std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl; @@ -4604,6 +4588,20 @@ namespace Catch { return matchedTests; } + inline std::size_t listTestsNamesOnly( Config const& config ) { + std::size_t matchedTests = 0; + std::vector<TestCase> matchedTestCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases ); + for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + std::cout << testCaseInfo.name << std::endl; + } + return matchedTests; + } + inline std::size_t listTags( Config const& config ) { if( config.filters().empty() ) std::cout << "All available tags:\n"; @@ -4612,23 +4610,21 @@ namespace Catch { std::map<std::string, int> tagCounts; - std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); - for( std::vector<TestCase>::const_iterator it = allTests.begin(), - itEnd = allTests.end(); + std::vector<TestCase> matchedTestCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( config, matchedTestCases ); + for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); it != itEnd; ++it ) { - if( matchesFilters( config.filters(), *it ) ) { - for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::map<std::string, int>::iterator countIt = tagCounts.find( tagName ); - if( countIt == tagCounts.end() ) - tagCounts.insert( std::make_pair( tagName, 1 ) ); - else - countIt->second++; - } + for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::map<std::string, int>::iterator countIt = tagCounts.find( tagName ); + if( countIt == tagCounts.end() ) + tagCounts.insert( std::make_pair( tagName, 1 ) ); + else + countIt->second++; } } @@ -4675,6 +4671,8 @@ namespace Catch { Option<std::size_t> listedCount; if( config.listTests() ) listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); if( config.listTags() ) listedCount = listedCount.valueOr(0) + listTags( config ); if( config.listReporters() ) @@ -4715,13 +4713,18 @@ namespace SectionTracking { RunState runState() const { return m_runState; } - void addChild( std::string const& childName ) { - m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); + TrackedSection* findChild( std::string const& childName ) { + TrackedSections::iterator it = m_children.find( childName ); + return it != m_children.end() + ? &it->second + : NULL; } - TrackedSection* getChild( std::string const& childName ) { - return &m_children.find( childName )->second; + TrackedSection* acquireChild( std::string const& childName ) { + if( TrackedSection* child = findChild( childName ) ) + return child; + m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); + return findChild( childName ); } - void enter() { if( m_runState == NotStarted ) m_runState = Executing; @@ -4760,21 +4763,13 @@ namespace SectionTracking { {} bool enterSection( std::string const& name ) { - if( m_completedASectionThisRun ) + TrackedSection* child = m_currentSection->acquireChild( name ); + if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) return false; - if( m_currentSection->runState() == TrackedSection::Executing ) { - m_currentSection->addChild( name ); - return false; - } - else { - TrackedSection* child = m_currentSection->getChild( name ); - if( child->runState() != TrackedSection::Completed ) { - m_currentSection = child; - m_currentSection->enter(); - return true; - } - return false; - } + + m_currentSection = child; + m_currentSection->enter(); + return true; } void leaveSection() { m_currentSection->leave(); @@ -4792,9 +4787,7 @@ namespace SectionTracking { class Guard { public: - Guard( TestCaseTracker& tracker ) - : m_tracker( tracker ) - { + Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { m_tracker.enterTestCase(); } ~Guard() { @@ -4895,23 +4888,6 @@ namespace Catch { m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); } - Totals runMatching( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - - std::vector<TestCase> matchingTests = getRegistryHub().getTestCaseRegistry().getMatchingTestCases( testSpec ); - - Totals totals; - - testGroupStarting( testSpec, groupIndex, groupsCount ); - - std::vector<TestCase>::const_iterator it = matchingTests.begin(); - std::vector<TestCase>::const_iterator itEnd = matchingTests.end(); - for(; it != itEnd; ++it ) - totals += runTest( *it ); - - testGroupEnded( testSpec, totals, groupIndex, groupsCount ); - return totals; - } - Totals runTest( TestCase const& testCase ) { Totals prevTotals = m_totals; @@ -5095,8 +5071,8 @@ namespace Catch { } // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. - for( std::vector<UnfinishedSections>::const_iterator it = m_unfinishedSections.begin(), - itEnd = m_unfinishedSections.end(); + for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); it != itEnd; ++it ) sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); @@ -5150,17 +5126,17 @@ namespace Catch { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _buildNumber, - std::string const& _branchName ) + char const* const _branchName ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), buildNumber( _buildNumber ), branchName( _branchName ) {} - const unsigned int majorVersion; - const unsigned int minorVersion; - const unsigned int buildNumber; - const std::string branchName; + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const buildNumber; + char const* const branchName; private: void operator=( Version const& ); @@ -5204,29 +5180,29 @@ namespace Catch { } return totals; } - - Totals runTestsForGroup( RunContext& context, const TestCaseFilters& filterGroup ) { + Totals runTestsForGroup( RunContext& context, TestCaseFilters const& filterGroup ) { Totals totals; - std::vector<TestCase>::const_iterator it = getRegistryHub().getTestCaseRegistry().getAllTests().begin(); - std::vector<TestCase>::const_iterator itEnd = getRegistryHub().getTestCaseRegistry().getAllTests().end(); + + std::vector<TestCase> testCases; + getRegistryHub().getTestCaseRegistry().getFilteredTests( filterGroup, *m_config, testCases ); + int testsRunForGroup = 0; - for(; it != itEnd; ++it ) { - if( filterGroup.shouldInclude( *it ) ) { - testsRunForGroup++; - if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { + for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it ) { + testsRunForGroup++; + if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { - if( context.aborting() ) - break; + if( context.aborting() ) + break; - totals += context.runTest( *it ); - m_testsAlreadyRun.insert( *it ); - } + totals += context.runTest( *it ); + m_testsAlreadyRun.insert( *it ); } } if( testsRunForGroup == 0 && !filterGroup.getName().empty() ) m_reporter->noMatchingTestCases( filterGroup.getName() ); return totals; - } private: @@ -5286,7 +5262,7 @@ namespace Catch { std::cout << "\nCatch v" << libraryVersion.majorVersion << "." << libraryVersion.minorVersion << " build " << libraryVersion.buildNumber; - if( libraryVersion.branchName != "master" ) + if( libraryVersion.branchName != std::string( "master" ) ) std::cout << " (" << libraryVersion.branchName << " branch)"; std::cout << "\n"; @@ -5296,9 +5272,8 @@ namespace Catch { int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); - if( unusedOptionBehaviour == OnUnusedOptions::Fail ) - enforceNoUsedTokens(); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); @@ -5306,7 +5281,7 @@ namespace Catch { catch( std::exception& ex ) { { Colour colourGuard( Colour::Red ); - std::cerr << "\nError in input:\n" + std::cerr << "\nError(s) in input:\n" << Text( ex.what(), TextAttributes().setIndent(2) ) << "\n\n"; } @@ -5321,18 +5296,6 @@ namespace Catch { m_config.reset(); } - void enforceNoUsedTokens() const { - if( !m_unusedTokens.empty() ) { - std::vector<Clara::Parser::Token>::const_iterator - it = m_unusedTokens.begin(), - itEnd = m_unusedTokens.end(); - std::string msg; - for(; it != itEnd; ++it ) - msg += " unrecognised option: " + it->data + "\n"; - throw std::runtime_error( msg.substr( 0, msg.size()-1 ) ); - } - } - int run( int argc, char* const argv[] ) { int returnCode = applyCommandLine( argc, argv ); @@ -5422,9 +5385,12 @@ namespace Catch { } else { TestCase const& prev = *m_functions.find( testCase ); - std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" - << "\tFirst seen at " << SourceLineInfo( prev.getTestCaseInfo().lineInfo ) << "\n" - << "\tRedefined at " << SourceLineInfo( testCase.getTestCaseInfo().lineInfo ) << std::endl; + { + Colour colourGuard( Colour::Red ); + std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; + } exit(1); } } @@ -5437,32 +5403,24 @@ namespace Catch { return m_nonHiddenFunctions; } - // !TBD deprecated - virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const { - std::vector<TestCase> matchingTests; - getMatchingTestCases( rawTestSpec, matchingTests ); - return matchingTests; - } - - // !TBD deprecated - virtual void getMatchingTestCases( std::string const& rawTestSpec, std::vector<TestCase>& matchingTestsOut ) const { - TestCaseFilter filter( rawTestSpec ); - - std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(); - std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end(); - for(; it != itEnd; ++it ) { - if( filter.shouldInclude( *it ) ) { - matchingTestsOut.push_back( *it ); - } + virtual void getFilteredTests( TestCaseFilters const& filters, IConfig const& config, std::vector<TestCase>& matchingTestCases ) const { + for( std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(), + itEnd = m_functionsInOrder.end(); + it != itEnd; + ++it ) { + if( filters.shouldInclude( *it ) && ( config.allowThrows() || !it->throws() ) ) + matchingTestCases.push_back( *it ); } } - virtual void getMatchingTestCases( TestCaseFilters const& filters, std::vector<TestCase>& matchingTestsOut ) const { - std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(); - std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end(); - // !TBD: replace with algorithm - for(; it != itEnd; ++it ) - if( filters.shouldInclude( *it ) ) - matchingTestsOut.push_back( *it ); + virtual void getFilteredTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) const { + if( config.filters().empty() ) + return getFilteredTests( TestCaseFilters( "empty" ), config, matchingTestCases ); + + for( std::vector<TestCaseFilters>::const_iterator it = config.filters().begin(), + itEnd = config.filters().end(); + it != itEnd; + ++it ) + getFilteredTests( *it, config, matchingTestCases ); } private: @@ -5707,7 +5665,7 @@ namespace Catch { m_what = oss.str(); } - const char* NotImplementedException::what() const throw() { + const char* NotImplementedException::what() const CATCH_NOEXCEPT { return m_what.c_str(); } @@ -5716,6 +5674,89 @@ namespace Catch { // #included from: catch_context_impl.hpp #define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include <streambuf> + +namespace Catch { + + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; + }; +} + +#include <stdexcept> +#include <cstdio> + +namespace Catch { + + template<typename WriterF, size_t bufferSize=256> + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast<char>( c ) ) ); + else + sputc( static_cast<char>( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + Stream::Stream() + : streamBuf( NULL ), isOwned( false ) + {} + + Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) + : streamBuf( _streamBuf ), isOwned( _isOwned ) + {} + + void Stream::release() { + if( isOwned ) { + delete streamBuf; + streamBuf = NULL; + isOwned = false; + } + } +} + namespace Catch { class Context : public IMutableContext { @@ -5878,7 +5919,10 @@ namespace { return true; } - Win32ColourImpl platformColourImpl; + static Detail::IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + return &s_instance; + } } // end anon namespace } // end namespace Catch @@ -5922,10 +5966,13 @@ namespace { }; inline bool shouldUseColourForPlatform() { - return isatty( fileno(stdout) ); + return isatty(STDOUT_FILENO); } - PosixColourImpl platformColourImpl; + static Detail::IColourImpl* platformColourInstance() { + static PosixColourImpl s_instance; + return &s_instance; + } } // end anon namespace } // end namespace Catch @@ -5937,21 +5984,28 @@ namespace Catch { namespace { struct NoColourImpl : Detail::IColourImpl { void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } }; - NoColourImpl noColourImpl; - static const bool shouldUseColour = shouldUseColourForPlatform() && - !isDebuggerActive(); + static bool shouldUseColour() { + return shouldUseColourForPlatform() && !isDebuggerActive(); + } } Colour::Colour( Code _colourCode ){ use( _colourCode ); } Colour::~Colour(){ use( None ); } void Colour::use( Code _colourCode ) { - impl->use( _colourCode ); + impl()->use( _colourCode ); } - Detail::IColourImpl* Colour::impl = shouldUseColour - ? static_cast<Detail::IColourImpl*>( &platformColourImpl ) - : static_cast<Detail::IColourImpl*>( &noColourImpl ); + Detail::IColourImpl* Colour::impl() { + return shouldUseColour() + ? platformColourInstance() + : NoColourImpl::instance(); + } } // end namespace Catch @@ -6202,6 +6256,16 @@ namespace Catch { namespace Catch { + inline bool isSpecialTag( std::string const& tag ) { + return tag == "." || + tag == "hide" || + tag == "!hide" || + tag == "!throws"; + } + inline bool isReservedTag( std::string const& tag ) { + return !isSpecialTag( tag ) && tag.size() > 0 && !isalnum( tag[0] ); + } + TestCase makeTestCase( ITestCase* _testCase, std::string const& _className, std::string const& _name, @@ -6209,12 +6273,33 @@ namespace Catch { SourceLineInfo const& _lineInfo ) { std::string desc = _descOrTags; - bool isHidden( startsWith( _name, "./" ) ); + bool isHidden( startsWith( _name, "./" ) ); // Legacy support std::set<std::string> tags; TagExtracter( tags ).parse( desc ); + for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); + it != itEnd; + ++it ) + if( isReservedTag( *it ) ) { + { + Colour colourGuard( Colour::Red ); + std::cerr + << "Tag name [" << *it << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard( Colour::FileName ); + std::cerr << _lineInfo << std::endl; + } + exit(1); + } + if( tags.find( "hide" ) != tags.end() || tags.find( "." ) != tags.end() ) isHidden = true; + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } TestCaseInfo info( _name, _className, desc, tags, isHidden, _lineInfo ); return TestCase( _testCase, info ); } @@ -6230,11 +6315,15 @@ namespace Catch { description( _description ), tags( _tags ), lineInfo( _lineInfo ), - isHidden( _isHidden ) + isHidden( _isHidden ), + throws( false ) { std::ostringstream oss; - for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) + for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { oss << "[" << *it << "]"; + if( *it == "!throws" ) + throws = true; + } tagsAsString = oss.str(); } @@ -6245,7 +6334,8 @@ namespace Catch { tags( other.tags ), tagsAsString( other.tagsAsString ), lineInfo( other.lineInfo ), - isHidden( other.isHidden ) + isHidden( other.isHidden ), + throws( other.throws ) {} TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} @@ -6268,6 +6358,9 @@ namespace Catch { bool TestCase::isHidden() const { return TestCaseInfo::isHidden; } + bool TestCase::throws() const { + return TestCaseInfo::throws; + } bool TestCase::hasTag( std::string const& tag ) const { return tags.find( toLower( tag ) ) != tags.end(); @@ -6311,101 +6404,260 @@ namespace Catch { } // end namespace Catch -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED +// #included from: catch_tags.hpp +#define TWOBLUECUBES_CATCH_TAGS_HPP_INCLUDED namespace Catch { + TagParser::~TagParser() {} - // These numbers are maintained by a script - Version libraryVersion( 1, 0, 13, "master" ); -} + void TagParser::parse( std::string const& str ) { + std::size_t pos = 0; + while( pos < str.size() ) { + char c = str[pos]; + if( c == '[' ) { + std::size_t end = str.find_first_of( ']', pos ); + if( end != std::string::npos ) { + acceptTag( str.substr( pos+1, end-pos-1 ) ); + pos = end+1; + } + else { + acceptChar( c ); + pos++; + } + } + else { + acceptChar( c ); + pos++; + } + } + endParse(); + } -// #included from: catch_text.hpp -#define TWOBLUECUBES_CATCH_TEXT_HPP_INCLUDED + TagExtracter::TagExtracter( std::set<std::string>& tags ) + : m_tags( tags ) + {} -#include <string> -#include <vector> + TagExtracter::~TagExtracter() {} + + void TagExtracter::parse( std::string& description ) { + TagParser::parse( description ); + description = m_remainder; + } + + void TagExtracter::acceptTag( std::string const& tag ) { + m_tags.insert( toLower( tag ) ); + } + void TagExtracter::acceptChar( char c ) { + m_remainder += c; + } + + Tag::Tag() : m_isNegated( false ) {} + Tag::Tag( std::string const& name, bool isNegated ) + : m_name( name ), + m_isNegated( isNegated ) + {} + + std::string Tag::getName() const { + return m_name; + } + bool Tag::isNegated() const { + return m_isNegated; + } + + bool Tag::operator ! () const { + return m_name.empty(); + } + + void TagSet::add( Tag const& tag ) { + m_tags.insert( std::make_pair( toLower( tag.getName() ), tag ) ); + } + + bool TagSet::empty() const { + return m_tags.empty(); + } + + bool TagSet::matches( std::set<std::string> const& tags ) const { + for( TagMap::const_iterator + it = m_tags.begin(), itEnd = m_tags.end(); + it != itEnd; + ++it ) { + bool found = tags.find( it->first ) != tags.end(); + if( found == it->second.isNegated() ) + return false; + } + return true; + } + + bool TagExpression::matches( std::set<std::string> const& tags ) const { + for( std::vector<TagSet>::const_iterator + it = m_tagSets.begin(), itEnd = m_tagSets.end(); + it != itEnd; + ++it ) + if( it->matches( tags ) ) + return true; + return false; + } + + TagExpressionParser::TagExpressionParser( TagExpression& exp ) + : m_isNegated( false ), + m_exp( exp ) + {} + + TagExpressionParser::~TagExpressionParser() {} + + void TagExpressionParser::acceptTag( std::string const& tag ) { + m_currentTagSet.add( Tag( tag, m_isNegated ) ); + m_isNegated = false; + } + + void TagExpressionParser::acceptChar( char c ) { + switch( c ) { + case '~': + m_isNegated = true; + break; + case ',': + m_exp.m_tagSets.push_back( m_currentTagSet ); + m_currentTagSet = TagSet(); + break; + } + } + + void TagExpressionParser::endParse() { + if( !m_currentTagSet.empty() ) + m_exp.m_tagSets.push_back( m_currentTagSet ); + } + +} // end namespace Catch + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED namespace Catch { - Text::Text( std::string const& _str, TextAttributes const& _attr ) - : attr( _attr ) + TestCaseFilter::TestCaseFilter( std::string const& testSpec, IfFilterMatches::DoWhat matchBehaviour ) + : m_stringToMatch( toLower( testSpec ) ), + m_filterType( matchBehaviour ), + m_wildcardPosition( NoWildcard ) { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; + if( m_filterType == IfFilterMatches::AutoDetectBehaviour ) { + if( startsWith( m_stringToMatch, "exclude:" ) ) { + m_stringToMatch = m_stringToMatch.substr( 8 ); + m_filterType = IfFilterMatches::ExcludeTests; } - 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 if( startsWith( m_stringToMatch, "~" ) ) { + m_stringToMatch = m_stringToMatch.substr( 1 ); + m_filterType = IfFilterMatches::ExcludeTests; } 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 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; + m_filterType = IfFilterMatches::IncludeTests; } } + + if( startsWith( m_stringToMatch, "*" ) ) { + m_stringToMatch = m_stringToMatch.substr( 1 ); + m_wildcardPosition = (WildcardPosition)( m_wildcardPosition | WildcardAtStart ); + } + if( endsWith( m_stringToMatch, "*" ) ) { + m_stringToMatch = m_stringToMatch.substr( 0, m_stringToMatch.size()-1 ); + m_wildcardPosition = (WildcardPosition)( m_wildcardPosition | WildcardAtEnd ); + } } - void Text::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 ); + IfFilterMatches::DoWhat TestCaseFilter::getFilterType() const { + return m_filterType; } - std::string Text::toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); + bool TestCaseFilter::shouldInclude( TestCase const& testCase ) const { + return isMatch( testCase ) == (m_filterType == IfFilterMatches::IncludeTests); } - std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool TestCaseFilter::isMatch( TestCase const& testCase ) const { + std::string name = testCase.getTestCaseInfo().name; + toLowerInPlace( name ); + + switch( m_wildcardPosition ) { + case NoWildcard: + return m_stringToMatch == name; + case WildcardAtStart: + return endsWith( name, m_stringToMatch ); + case WildcardAtEnd: + return startsWith( name, m_stringToMatch ); + case WildcardAtBothEnds: + return contains( name, m_stringToMatch ); } - return _stream; + throw std::logic_error( "Unhandled wildcard type" ); } -} // end namespace Catch +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + TestCaseFilters::TestCaseFilters( std::string const& name ) : m_name( name ) {} + + std::string TestCaseFilters::getName() const { + return m_name; + } + + void TestCaseFilters::addFilter( TestCaseFilter const& filter ) { + if( filter.getFilterType() == IfFilterMatches::ExcludeTests ) + m_exclusionFilters.push_back( filter ); + else + m_inclusionFilters.push_back( filter ); + } + + void TestCaseFilters::addTags( std::string const& tagPattern ) { + TagExpression exp; + TagExpressionParser( exp ).parse( tagPattern ); + + m_tagExpressions.push_back( exp ); + } + + bool TestCaseFilters::shouldInclude( TestCase const& testCase ) const { + if( !m_tagExpressions.empty() ) { + std::vector<TagExpression>::const_iterator it = m_tagExpressions.begin(); + std::vector<TagExpression>::const_iterator itEnd = m_tagExpressions.end(); + for(; it != itEnd; ++it ) + if( it->matches( testCase.getTags() ) ) + break; + if( it == itEnd ) + return false; + } + + if( !m_inclusionFilters.empty() ) { + std::vector<TestCaseFilter>::const_iterator it = m_inclusionFilters.begin(); + std::vector<TestCaseFilter>::const_iterator itEnd = m_inclusionFilters.end(); + for(; it != itEnd; ++it ) + if( it->shouldInclude( testCase ) ) + break; + if( it == itEnd ) + return false; + } + else if( m_exclusionFilters.empty() && m_tagExpressions.empty() ) { + return !testCase.isHidden(); + } + + std::vector<TestCaseFilter>::const_iterator it = m_exclusionFilters.begin(); + std::vector<TestCaseFilter>::const_iterator itEnd = m_exclusionFilters.end(); + for(; it != itEnd; ++it ) + if( !it->shouldInclude( testCase ) ) + return false; + return true; + } +} + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + + // These numbers are maintained by a script + Version libraryVersion( 1, 0, 43, "master" ); +} // #included from: catch_message.hpp #define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED @@ -6446,6 +6698,26 @@ namespace Catch { namespace Catch { + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; + class LegacyReporterAdapter : public SharedImpl<IStreamingReporter> { public: @@ -6593,9 +6865,507 @@ namespace Catch { #ifdef __clang__ #pragma clang diagnostic pop #endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == 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; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + 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 ) : ""; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << " " << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << "s"; + return os; + } + + SourceLineInfo::SourceLineInfo() : 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(); + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && file == other.file; + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << "(" << info.line << ")"; +#else + 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 << "'"; + if( isTrue( true )) + throw std::logic_error( oss.str() ); + } +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + + Section::Section( SourceLineInfo const& lineInfo, + std::string const& name, + std::string const& description ) + : m_info( name, description, lineInfo ), + m_sectionIncluded( getCurrentContext().getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) + getCurrentContext().getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); + } + + // This indicates whether the section should be executed or not + Section::operator bool() { + return m_sectionIncluded; + } + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#include <iostream> + +#ifdef CATCH_PLATFORM_MAC + + #include <assert.h> + #include <stdbool.h> + #include <sys/types.h> + #include <unistd.h> + #include <sys/sysctl.h> + + namespace Catch{ + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { + std::cerr << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + inline bool isDebuggerActive() { return false; } + } +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS + extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + std::cout << text; + } + } +#endif // Platform + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +namespace Catch { + +std::string toString( std::string const& value ) { + std::string s = value; + if( getCurrentContext().getConfig()->showInvisibles() ) { + for(size_t i = 0; i < s.size(); ++i ) { + std::string subs; + switch( s[i] ) { + case '\n': subs = "\\n"; break; + case '\t': subs = "\\t"; break; + default: break; + } + if( !subs.empty() ) { + s = s.substr( 0, i ) + subs + s.substr( i+1 ); + ++i; + } + } + } + return "\"" + s + "\""; +} +std::string toString( std::wstring const& value ) { + + std::string s; + s.reserve( value.size() ); + for(size_t i = 0; i < value.size(); ++i ) + s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?'; + return toString( s ); +} + +std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); +} + +std::string toString( char* const value ) { + return Catch::toString( static_cast<const char*>( value ) ); +} + +std::string toString( int value ) { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +std::string toString( unsigned long value ) { + std::ostringstream oss; + if( value > 8192 ) + oss << "0x" << std::hex << value; + else + oss << value; + return oss.str(); +} + +std::string toString( unsigned int value ) { + return toString( static_cast<unsigned long>( value ) ); +} + +std::string toString( const double value ) { + std::ostringstream oss; + oss << std::setprecision( 10 ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +std::string toString( bool value ) { + return value ? "true" : "false"; +} + +std::string toString( char value ) { + return value < ' ' + ? toString( static_cast<unsigned int>( value ) ) + : Detail::makeString( value ); +} + +std::string toString( signed char value ) { + return toString( static_cast<char>( value ) ); +} + +std::string toString( unsigned char value ) { + return toString( static_cast<char>( value ) ); +} + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return std::string( "@\"" ) + [nsstring UTF8String] + "\""; + } + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return std::string( "@\"" ) + [nsstring UTF8String] + "\""; + } + std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } +#endif + +} // end namespace Catch + // #included from: ../reporters/catch_reporter_xml.hpp #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +namespace Catch { + + struct StreamingReporterBase : SharedImpl<IStreamingReporter> { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + {} + + virtual ~StreamingReporterBase(); + + virtual void noMatchingTestCases( std::string const& ) {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { + currentTestCaseInfo.reset(); + assert( m_sectionStack.empty() ); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + Ptr<IConfig> m_config; + std::ostream& stream; + + LazyStat<TestRunInfo> currentTestRunInfo; + LazyStat<GroupInfo> currentGroupInfo; + LazyStat<TestCaseInfo> currentTestCaseInfo; + + std::vector<SectionInfo> m_sectionStack; + }; + + struct CumulativeReporterBase : SharedImpl<IStreamingReporter> { + template<typename T, typename ChildNodeT> + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + typedef std::vector<Ptr<ChildNodeT> > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); + + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == ( Ptr<SectionNode> const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector<Ptr<SectionNode> > ChildSections; + typedef std::vector<AssertionStats> Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + bool operator() ( Ptr<SectionNode> const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + BySectionInfo& operator=( BySectionInfo const& other ); // = delete; + + SectionInfo const& m_other; + }; + + typedef Node<TestCaseStats, SectionNode> TestCaseNode; + typedef Node<TestGroupStats, TestCaseNode> TestGroupNode; + typedef Node<TestRunStats, TestGroupNode> TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + {} + ~CumulativeReporterBase(); + + virtual void testRunStarting( TestRunInfo const& ) {} + virtual void testGroupStarting( GroupInfo const& ) {} + + virtual void testCaseStarting( TestCaseInfo const& ) {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr<SectionNode> node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) { + Ptr<TestRunNode> node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + Ptr<IConfig> m_config; + std::ostream& stream; + std::vector<AssertionStats> m_assertions; + std::vector<std::vector<Ptr<SectionNode> > > m_sections; + std::vector<Ptr<TestCaseNode> > m_testCases; + std::vector<Ptr<TestGroupNode> > m_testGroups; + + std::vector<Ptr<TestRunNode> > m_testRuns; + + Ptr<SectionNode> m_rootSection; + Ptr<SectionNode> m_deepestSection; + std::vector<Ptr<SectionNode> > m_sectionStack; + + }; + +} // end namespace Catch + // #included from: ../internal/catch_reporter_registrars.hpp #define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED @@ -6720,11 +7490,18 @@ namespace Catch { endElement(); } +# ifndef CATCH_CPP11_OR_GREATER XmlWriter& operator = ( XmlWriter const& other ) { XmlWriter temp( other ); swap( temp ); return *this; } +# else + XmlWriter( XmlWriter const& ) = default; + XmlWriter( XmlWriter && ) = default; + XmlWriter& operator = ( XmlWriter const& ) = default; + XmlWriter& operator = ( XmlWriter && ) = default; +# endif void swap( XmlWriter& other ) { std::swap( m_tagIsOpen, other.m_tagIsOpen ); @@ -6907,7 +7684,7 @@ namespace Catch { virtual void StartSection( const std::string& sectionName, const std::string& description ) { if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) - .writeAttribute( "name", sectionName ) + .writeAttribute( "name", trim( sectionName ) ) .writeAttribute( "description", description ); } } @@ -6924,7 +7701,7 @@ namespace Catch { } virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) { - m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); + m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); m_currentTestSuccess = true; } @@ -7055,7 +7832,7 @@ namespace Catch { writeGroup( *m_testGroups.back(), suiteTime ); } - virtual void testRunEnded() { + virtual void testRunEndedCumulative() { xml.endElement(); } @@ -7211,6 +7988,8 @@ namespace Catch { // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#include <cstring> + namespace Catch { struct ConsoleReporter : StreamingReporterBase { @@ -7454,13 +8233,13 @@ namespace Catch { m_atLeastOneTestCasePrinted = true; } void lazyPrintRunInfo() { - stream << "\n" << getTildes() << "\n"; + stream << "\n" << getLineOfChars<'~'>() << "\n"; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion.majorVersion << "." << libraryVersion.minorVersion << " b" << libraryVersion.buildNumber; - if( libraryVersion.branchName != "master" ) + if( libraryVersion.branchName != std::string( "master" ) ) stream << " (" << libraryVersion.branchName << ")"; stream << " host application.\n" << "Run with -? for options\n\n"; @@ -7490,19 +8269,19 @@ namespace Catch { SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; if( !lineInfo.empty() ){ - stream << getDashes() << "\n"; + stream << getLineOfChars<'-'>() << "\n"; Colour colourGuard( Colour::FileName ); stream << lineInfo << "\n"; } - stream << getDots() << "\n" << std::endl; + stream << getLineOfChars<'.'>() << "\n" << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); - stream << getDots() << "\n"; + stream << getLineOfChars<'.'>() << "\n"; } void printOpenHeader( std::string const& _name ) { - stream << getDashes() << "\n"; + stream << getLineOfChars<'-'>() << "\n"; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); @@ -7575,26 +8354,19 @@ namespace Catch { } void printTotalsDivider() { - stream << getDoubleDashes() << "\n"; + stream << getLineOfChars<'='>() << "\n"; } void printSummaryDivider() { - stream << getDashes() << "\n"; - } - static std::string const& getDashes() { - static const std::string dashes( CATCH_CONFIG_CONSOLE_WIDTH-1, '-' ); - return dashes; - } - static std::string const& getDots() { - static const std::string dots( CATCH_CONFIG_CONSOLE_WIDTH-1, '.' ); - return dots; + stream << getLineOfChars<'-'>() << "\n"; } - static std::string const& getDoubleDashes() { - static const std::string doubleDashes( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); - return doubleDashes; - } - static std::string const& getTildes() { - static const std::string dots( CATCH_CONFIG_CONSOLE_WIDTH-1, '~' ); - return dots; + template<char C> + static char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; } private: @@ -7606,10 +8378,291 @@ namespace Catch { } // end namespace Catch +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + CompactReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual ~CompactReporter(); + + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , stats( _stats ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + // Colour::LightGrey + + static Colour dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ":"; + } + + void printResultType( Colour colour, std::string passOrFail ) const { + if( !passOrFail.empty() ) { + { + Colour colourGuard( colour ); + stream << " " << passOrFail; + } + stream << ":"; + } + } + + void printIssue( std::string issue ) const { + stream << " " << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ";"; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages( Colour colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields compilation error: + std::vector<MessageInfo>::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + 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 << "'"; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector<MessageInfo> messages; + std::vector<MessageInfo>::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - 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 " ; + } + + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : ""; + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "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" ) << "."; + } + } + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch + namespace Catch { NonCopyable::~NonCopyable() {} IShared::~IShared() {} - StreamBufBase::~StreamBufBase() throw() {} + StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} IContext::~IContext() {} IResultCapture::~IResultCapture() {} ITestCase::~ITestCase() {} @@ -7632,6 +8685,7 @@ namespace Catch { StreamingReporterBase::~StreamingReporterBase() {} ConsoleReporter::~ConsoleReporter() {} + CompactReporter::~CompactReporter() {} IRunner::~IRunner() {} IMutableContext::~IMutableContext() {} IConfig::~IConfig() {} @@ -7641,9 +8695,6 @@ namespace Catch { FreeFunctionTestCase::~FreeFunctionTestCase() {} IGeneratorInfo::~IGeneratorInfo() {} IGeneratorsForTest::~IGeneratorsForTest() {} - TagParser::~TagParser() {} - TagExtracter::~TagExtracter() {} - TagExpressionParser::~TagExpressionParser() {} Matchers::Impl::StdString::Equals::~Equals() {} Matchers::Impl::StdString::Contains::~Contains() {} @@ -7694,6 +8745,10 @@ int main (int argc, char * const argv[]) { #endif +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ @@ -7720,9 +8775,7 @@ int main (int argc, char * const argv[]) { #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN" ) -#define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL" ) -#define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED" ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) #define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) #define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) #define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) @@ -7732,11 +8785,15 @@ int main (int argc, char * const argv[]) { #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -7781,9 +8838,7 @@ int main (int argc, char * const argv[]) { #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) #define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN" ) -#define FAIL( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL" ) -#define SUCCEED( msg ) INTERNAL_CATCH_MSG( msg, Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED" ) +#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) #define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) #define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) #define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) @@ -7793,11 +8848,15 @@ int main (int argc, char * const argv[]) { #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) #else #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) |