mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 12:17:11 +01:00 
			
		
		
		
	Merged from develop branch
- now v1.3.0
This commit is contained in:
		| @@ -66,16 +66,16 @@ | ||||
|     } while( Catch::alwaysFalse() ) | ||||
|  | ||||
| /////////////////////////////////////////////////////////////////////////////// | ||||
| #define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \ | ||||
| #define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ | ||||
|     do { \ | ||||
|         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ | ||||
|         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ | ||||
|         if( __catchResult.allowThrows() ) \ | ||||
|             try { \ | ||||
|                 expr; \ | ||||
|                 __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ | ||||
|             } \ | ||||
|             catch( ... ) { \ | ||||
|                 __catchResult.captureResult( Catch::ResultWas::Ok ); \ | ||||
|                 __catchResult.captureExpectedException( matcher ); \ | ||||
|             } \ | ||||
|         else \ | ||||
|             __catchResult.captureResult( Catch::ResultWas::Ok ); \ | ||||
| @@ -131,12 +131,12 @@ | ||||
|     do { \ | ||||
|         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \ | ||||
|         try { \ | ||||
|             std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \ | ||||
|             std::string matcherAsString = (matcher).toString(); \ | ||||
|             __catchResult \ | ||||
|                 .setLhs( Catch::toString( arg ) ) \ | ||||
|                 .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ | ||||
|                 .setOp( "matches" ) \ | ||||
|                 .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \ | ||||
|                 .setResultType( (matcher).match( arg ) ); \ | ||||
|             __catchResult.captureExpression(); \ | ||||
|         } catch( ... ) { \ | ||||
|             __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ | ||||
|   | ||||
| @@ -23,6 +23,7 @@ namespace Catch { | ||||
|         config.abortAfter = x; | ||||
|     } | ||||
|     inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } | ||||
|     inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } | ||||
|  | ||||
|     inline void addWarning( ConfigData& config, std::string const& _warning ) { | ||||
|         if( _warning == "NoAssertions" ) | ||||
| @@ -116,7 +117,7 @@ namespace Catch { | ||||
|         cli["-r"]["--reporter"] | ||||
| //            .placeholder( "name[:filename]" ) | ||||
|             .describe( "reporter to use (defaults to console)" ) | ||||
|             .bind( &ConfigData::reporterName, "name" ); | ||||
|             .bind( &addReporterName, "name" ); | ||||
|  | ||||
|         cli["-n"]["--name"] | ||||
|             .describe( "suite name" ) | ||||
| @@ -153,12 +154,16 @@ namespace Catch { | ||||
|             .describe( "load test names to run from a file" ) | ||||
|             .bind( &loadTestNamesFromFile, "filename" ); | ||||
|  | ||||
|         cli["-#"]["--filenames-as-tags"] | ||||
|             .describe( "adds a tag for the filename" ) | ||||
|             .bind( &ConfigData::filenamesAsTags ); | ||||
|  | ||||
|         // 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"]  | ||||
|         cli["--list-reporters"] | ||||
|             .describe( "list all reporters" ) | ||||
|             .bind( &ConfigData::listReporters ); | ||||
|  | ||||
|   | ||||
| @@ -23,6 +23,13 @@ | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     struct IConfig; | ||||
|  | ||||
|     struct CaseSensitive { enum Choice { | ||||
|         Yes, | ||||
|         No | ||||
|     }; }; | ||||
|  | ||||
|     class NonCopyable { | ||||
| #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS | ||||
|         NonCopyable( NonCopyable const& )              = delete; | ||||
| @@ -109,6 +116,9 @@ namespace Catch { | ||||
|  | ||||
|     void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); | ||||
|  | ||||
|     void seedRng( IConfig const& config ); | ||||
|     unsigned int rngSeed(); | ||||
|  | ||||
|     // Use this in variadic streaming macros to allow | ||||
|     //    >> +StreamEndStop | ||||
|     // as well as | ||||
|   | ||||
| @@ -36,7 +36,7 @@ namespace Catch { | ||||
|  | ||||
|         return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { | ||||
|         bool replaced = false; | ||||
|         std::size_t i = str.find( replaceThis ); | ||||
| @@ -50,7 +50,7 @@ namespace Catch { | ||||
|         } | ||||
|         return replaced; | ||||
|     } | ||||
|      | ||||
|  | ||||
|     pluralise::pluralise( std::size_t count, std::string const& label ) | ||||
|     :   m_count( count ), | ||||
|         m_label( label ) | ||||
| @@ -82,6 +82,14 @@ namespace Catch { | ||||
|         return line < other.line || ( line == other.line  && file < other.file ); | ||||
|     } | ||||
|  | ||||
|     void seedRng( IConfig const& config ) { | ||||
|         if( config.rngSeed() != 0 ) | ||||
|             std::srand( config.rngSeed() ); | ||||
|     } | ||||
|     unsigned int rngSeed() { | ||||
|         return getCurrentContext().getConfig()->rngSeed(); | ||||
|     } | ||||
|  | ||||
|     std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { | ||||
| #ifndef __GNUG__ | ||||
|         os << info.file << "(" << info.line << ")"; | ||||
|   | ||||
| @@ -16,11 +16,18 @@ | ||||
| // CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods | ||||
| // CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? | ||||
| // CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported | ||||
| // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? | ||||
| // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? | ||||
| // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) | ||||
|  | ||||
| // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? | ||||
|  | ||||
| // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? | ||||
|  | ||||
| // **************** | ||||
| // Note to maintainers: if new toggles are added please document them | ||||
| // in configuration.md, too | ||||
| // **************** | ||||
|  | ||||
| // In general each macro has a _NO_<feature name> form | ||||
| // (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. | ||||
| @@ -66,10 +73,13 @@ | ||||
| // GCC | ||||
| #ifdef __GNUC__ | ||||
|  | ||||
| #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) | ||||
| #if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) | ||||
| #   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR | ||||
| #endif | ||||
|  | ||||
| // - otherwise more recent versions define __cplusplus >= 201103L | ||||
| // and will get picked up below | ||||
|  | ||||
|  | ||||
| #endif // __GNUC__ | ||||
|  | ||||
| @@ -79,6 +89,7 @@ | ||||
|  | ||||
| #if (_MSC_VER >= 1600) | ||||
| #   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR | ||||
| #   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR | ||||
| #endif | ||||
|  | ||||
| #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) | ||||
| @@ -88,6 +99,8 @@ | ||||
|  | ||||
| #endif // _MSC_VER | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| // Use variadic macros if the compiler supports them | ||||
| #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ | ||||
|     ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ | ||||
| @@ -102,7 +115,7 @@ | ||||
| // C++ language feature support | ||||
|  | ||||
| // catch all support for C++11 | ||||
| #if (__cplusplus >= 201103L) | ||||
| #if defined(__cplusplus) && __cplusplus >= 201103L | ||||
|  | ||||
| #  define CATCH_CPP11_OR_GREATER | ||||
|  | ||||
| @@ -130,6 +143,18 @@ | ||||
| #    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS | ||||
| #  endif | ||||
|  | ||||
| #  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) | ||||
| #    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG | ||||
| #  endif | ||||
|  | ||||
| #  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) | ||||
| #    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE | ||||
| #  endif | ||||
| #  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) | ||||
| #    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR | ||||
| #  endif | ||||
|  | ||||
|  | ||||
| #endif // __cplusplus >= 201103L | ||||
|  | ||||
| // Now set the actual defines based on the above + anything the user has configured | ||||
| @@ -149,7 +174,16 @@ | ||||
| #   define CATCH_CONFIG_CPP11_TUPLE | ||||
| #endif | ||||
| #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) | ||||
| #define CATCH_CONFIG_VARIADIC_MACROS | ||||
| #   define CATCH_CONFIG_VARIADIC_MACROS | ||||
| #endif | ||||
| #if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) | ||||
| #   define CATCH_CONFIG_CPP11_LONG_LONG | ||||
| #endif | ||||
| #if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) | ||||
| #   define CATCH_CONFIG_CPP11_OVERRIDE | ||||
| #endif | ||||
| #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) | ||||
| #   define CATCH_CONFIG_CPP11_UNIQUE_PTR | ||||
| #endif | ||||
|  | ||||
|  | ||||
| @@ -162,6 +196,26 @@ | ||||
| #  define CATCH_NOEXCEPT_IS(x) | ||||
| #endif | ||||
|  | ||||
| // nullptr support | ||||
| #ifdef CATCH_CONFIG_CPP11_NULLPTR | ||||
| #   define CATCH_NULL nullptr | ||||
| #else | ||||
| #   define CATCH_NULL NULL | ||||
| #endif | ||||
|  | ||||
| // override support | ||||
| #ifdef CATCH_CONFIG_CPP11_OVERRIDE | ||||
| #   define CATCH_OVERRIDE override | ||||
| #else | ||||
| #   define CATCH_OVERRIDE | ||||
| #endif | ||||
|  | ||||
| // unique_ptr support | ||||
| #ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR | ||||
| #   define CATCH_AUTO_PTR( T ) std::unique_ptr<T> | ||||
| #else | ||||
| #   define CATCH_AUTO_PTR( T ) std::auto_ptr<T> | ||||
| #endif | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED | ||||
|  | ||||
|   | ||||
| @@ -38,6 +38,7 @@ namespace Catch { | ||||
|             showHelp( false ), | ||||
|             showInvisibles( false ), | ||||
|             forceColour( false ), | ||||
|             filenamesAsTags( false ), | ||||
|             abortAfter( -1 ), | ||||
|             rngSeed( 0 ), | ||||
|             verbosity( Verbosity::Normal ), | ||||
| @@ -57,6 +58,7 @@ namespace Catch { | ||||
|         bool showHelp; | ||||
|         bool showInvisibles; | ||||
|         bool forceColour; | ||||
|         bool filenamesAsTags; | ||||
|  | ||||
|         int abortAfter; | ||||
|         unsigned int rngSeed; | ||||
| @@ -66,11 +68,11 @@ namespace Catch { | ||||
|         ShowDurations::OrNot showDurations; | ||||
|         RunTests::InWhatOrder runOrder; | ||||
|  | ||||
|         std::string reporterName; | ||||
|         std::string outputFilename; | ||||
|         std::string name; | ||||
|         std::string processName; | ||||
|  | ||||
|         std::vector<std::string> reporterNames; | ||||
|         std::vector<std::string> testsOrTags; | ||||
|     }; | ||||
|  | ||||
| @@ -83,12 +85,11 @@ namespace Catch { | ||||
|     public: | ||||
|  | ||||
|         Config() | ||||
|         :   m_os( Catch::cout().rdbuf() ) | ||||
|         {} | ||||
|  | ||||
|         Config( ConfigData const& data ) | ||||
|         :   m_data( data ), | ||||
|             m_os( Catch::cout().rdbuf() ) | ||||
|             m_stream( openStream() ) | ||||
|         { | ||||
|             if( !data.testsOrTags.empty() ) { | ||||
|                 TestSpecParser parser( ITagAliasRegistry::get() ); | ||||
| @@ -99,12 +100,6 @@ namespace Catch { | ||||
|         } | ||||
|  | ||||
|         virtual ~Config() { | ||||
|             m_os.rdbuf( Catch::cout().rdbuf() ); | ||||
|             m_stream.release(); | ||||
|         } | ||||
|  | ||||
|         void setFilename( std::string const& filename ) { | ||||
|             m_data.outputFilename = filename; | ||||
|         } | ||||
|  | ||||
|         std::string const& getFilename() const { | ||||
| @@ -120,18 +115,7 @@ namespace Catch { | ||||
|  | ||||
|         bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } | ||||
|  | ||||
|         void setStreamBuf( std::streambuf* buf ) { | ||||
|             m_os.rdbuf( buf ? buf : Catch::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; } | ||||
|         std::vector<std::string> getReporterNames() const { return m_data.reporterNames; } | ||||
|  | ||||
|         int abortAfter() const { return m_data.abortAfter; } | ||||
|  | ||||
| @@ -142,7 +126,7 @@ namespace Catch { | ||||
|  | ||||
|         // IConfig interface | ||||
|         virtual bool allowThrows() const        { return !m_data.noThrow; } | ||||
|         virtual std::ostream& stream() const    { return m_os; } | ||||
|         virtual std::ostream& stream() const    { return m_stream->stream(); } | ||||
|         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; } | ||||
| @@ -152,14 +136,25 @@ namespace Catch { | ||||
|         virtual bool forceColour() const { return m_data.forceColour; } | ||||
|  | ||||
|     private: | ||||
|  | ||||
|         IStream const* openStream() { | ||||
|             if( m_data.outputFilename.empty() ) | ||||
|                 return new CoutStream(); | ||||
|             else if( m_data.outputFilename[0] == '%' ) { | ||||
|                 if( m_data.outputFilename == "%debug" ) | ||||
|                     return new DebugOutStream(); | ||||
|                 else | ||||
|                     throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); | ||||
|             } | ||||
|             else | ||||
|                 return new FileStream( m_data.outputFilename ); | ||||
|         } | ||||
|         ConfigData m_data; | ||||
|  | ||||
|         Stream m_stream; | ||||
|         mutable std::ostream m_os; | ||||
|         std::auto_ptr<IStream const> m_stream; | ||||
|         TestSpec m_testSpec; | ||||
|     }; | ||||
|  | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED | ||||
|   | ||||
| @@ -60,12 +60,13 @@ namespace { | ||||
|         { | ||||
|             CONSOLE_SCREEN_BUFFER_INFO csbiInfo; | ||||
|             GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); | ||||
|             originalAttributes = csbiInfo.wAttributes; | ||||
|             originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); | ||||
|             originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); | ||||
|         } | ||||
|  | ||||
|         virtual void use( Colour::Code _colourCode ) { | ||||
|             switch( _colourCode ) { | ||||
|                 case Colour::None:      return setTextAttribute( originalAttributes ); | ||||
|                 case Colour::None:      return setTextAttribute( originalForegroundAttributes ); | ||||
|                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); | ||||
|                 case Colour::Red:       return setTextAttribute( FOREGROUND_RED ); | ||||
|                 case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN ); | ||||
| @@ -85,10 +86,11 @@ namespace { | ||||
|  | ||||
|     private: | ||||
|         void setTextAttribute( WORD _textAttribute ) { | ||||
|             SetConsoleTextAttribute( stdoutHandle, _textAttribute ); | ||||
|             SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); | ||||
|         } | ||||
|         HANDLE stdoutHandle; | ||||
|         WORD originalAttributes; | ||||
|         WORD originalForegroundAttributes; | ||||
|         WORD originalBackgroundAttributes; | ||||
|     }; | ||||
|  | ||||
|     IColourImpl* platformColourInstance() { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED | ||||
|  | ||||
| #include "catch_runner_impl.hpp" | ||||
| #include "catch_run_context.hpp" | ||||
|  | ||||
| #include "catch_context.h" | ||||
| #include "catch_stream.hpp" | ||||
| @@ -17,7 +17,7 @@ namespace Catch { | ||||
|  | ||||
|     class Context : public IMutableContext { | ||||
|  | ||||
|         Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} | ||||
|         Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} | ||||
|         Context( Context const& ); | ||||
|         void operator=( Context const& ); | ||||
|  | ||||
| @@ -63,7 +63,7 @@ namespace Catch { | ||||
|                 m_generatorsByTestName.find( testName ); | ||||
|             return it != m_generatorsByTestName.end() | ||||
|                 ? it->second | ||||
|                 : NULL; | ||||
|                 : CATCH_NULL; | ||||
|         } | ||||
|  | ||||
|         IGeneratorsForTest& getGeneratorsForCurrentTest() { | ||||
| @@ -84,7 +84,7 @@ namespace Catch { | ||||
|     }; | ||||
|  | ||||
|     namespace { | ||||
|         Context* currentContext = NULL; | ||||
|         Context* currentContext = CATCH_NULL; | ||||
|     } | ||||
|     IMutableContext& getCurrentMutableContext() { | ||||
|         if( !currentContext ) | ||||
| @@ -95,17 +95,9 @@ namespace Catch { | ||||
|         return getCurrentMutableContext(); | ||||
|     } | ||||
|  | ||||
|     Stream createStream( std::string const& streamName ) { | ||||
|         if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false ); | ||||
|         if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false ); | ||||
|         if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true ); | ||||
|  | ||||
|         throw std::domain_error( "Unknown stream: " + streamName ); | ||||
|     } | ||||
|  | ||||
|     void cleanUpContext() { | ||||
|         delete currentContext; | ||||
|         currentContext = NULL; | ||||
|         currentContext = CATCH_NULL; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -50,7 +50,7 @@ | ||||
|             // Call sysctl. | ||||
|  | ||||
|             size = sizeof(info); | ||||
|             if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { | ||||
|             if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { | ||||
|                 Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; | ||||
|                 return false; | ||||
|             } | ||||
|   | ||||
| @@ -160,13 +160,51 @@ namespace Internal { | ||||
|         return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); | ||||
|     } | ||||
|  | ||||
| #ifdef CATCH_CONFIG_CPP11_LONG_LONG | ||||
|     // long long to unsigned X | ||||
|     template<Operator Op> bool compare( long long lhs, unsigned int rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op> bool compare( long long lhs, unsigned long rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op> bool compare( long long lhs, unsigned char rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); | ||||
|     } | ||||
|  | ||||
|     // unsigned long long to X | ||||
|     template<Operator Op> bool compare( unsigned long long lhs, int rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op> bool compare( unsigned long long lhs, long rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op> bool compare( unsigned long long lhs, char rhs ) { | ||||
|         return applyEvaluator<Op>( static_cast<long>( lhs ), rhs ); | ||||
|     } | ||||
|  | ||||
|     // pointer to long long (when comparing against NULL) | ||||
|     template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) { | ||||
|         return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs ); | ||||
|     } | ||||
|     template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) { | ||||
|         return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); | ||||
|     } | ||||
| #endif // CATCH_CONFIG_CPP11_LONG_LONG | ||||
|  | ||||
| #ifdef CATCH_CONFIG_CPP11_NULLPTR | ||||
|     // pointer to nullptr_t (when comparing against nullptr) | ||||
|     template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) { | ||||
|         return Evaluator<T*, T*, Op>::evaluate( NULL, rhs ); | ||||
|         return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs ); | ||||
|     } | ||||
|     template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) { | ||||
|         return Evaluator<T*, T*, Op>::evaluate( lhs, NULL ); | ||||
|         return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr ); | ||||
|     } | ||||
| #endif // CATCH_CONFIG_CPP11_NULLPTR | ||||
|  | ||||
|   | ||||
| @@ -32,13 +32,13 @@ namespace Catch { | ||||
| #ifdef __OBJC__ | ||||
|                 // In Objective-C try objective-c exceptions first | ||||
|                 @try { | ||||
|                     throw; | ||||
|                     return tryTranslators(); | ||||
|                 } | ||||
|                 @catch (NSException *exception) { | ||||
|                     return Catch::toString( [exception description] ); | ||||
|                 } | ||||
| #else | ||||
|                 throw; | ||||
|                 return tryTranslators(); | ||||
| #endif | ||||
|             } | ||||
|             catch( TestFailureException& ) { | ||||
| @@ -54,20 +54,15 @@ namespace Catch { | ||||
|                 return msg; | ||||
|             } | ||||
|             catch(...) { | ||||
|                 return tryTranslators( m_translators.begin() ); | ||||
|                 return "Unknown exception"; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const { | ||||
|             if( it == m_translators.end() ) | ||||
|                 return "Unknown exception"; | ||||
|  | ||||
|             try { | ||||
|                 return (*it)->translate(); | ||||
|             } | ||||
|             catch(...) { | ||||
|                 return tryTranslators( it+1 ); | ||||
|             } | ||||
|         std::string tryTranslators() const { | ||||
|             if( m_translators.empty() ) | ||||
|                 throw; | ||||
|             else | ||||
|                 return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
| #pragma clang diagnostic ignored "-Wweak-vtables" | ||||
| #endif | ||||
|  | ||||
| #include "../catch_runner.hpp" | ||||
| #include "../catch_session.hpp" | ||||
| #include "catch_registry_hub.hpp" | ||||
| #include "catch_notimplemented_exception.hpp" | ||||
| #include "catch_context_impl.hpp" | ||||
| @@ -35,15 +35,23 @@ | ||||
| #include "catch_tostring.hpp" | ||||
| #include "catch_result_builder.hpp" | ||||
| #include "catch_tag_alias_registry.hpp" | ||||
| #include "catch_test_case_tracker.hpp" | ||||
|  | ||||
| #include "../reporters/catch_reporter_multi.hpp" | ||||
| #include "../reporters/catch_reporter_xml.hpp" | ||||
| #include "../reporters/catch_reporter_junit.hpp" | ||||
| #include "../reporters/catch_reporter_console.hpp" | ||||
| #include "../reporters/catch_reporter_compact.hpp" | ||||
|  | ||||
| namespace Catch { | ||||
|     // These are all here to avoid warnings about not having any out of line | ||||
|     // virtual methods | ||||
|     NonCopyable::~NonCopyable() {} | ||||
|     IShared::~IShared() {} | ||||
|     IStream::~IStream() CATCH_NOEXCEPT {} | ||||
|     FileStream::~FileStream() CATCH_NOEXCEPT {} | ||||
|     CoutStream::~CoutStream() CATCH_NOEXCEPT {} | ||||
|     DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} | ||||
|     StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} | ||||
|     IContext::~IContext() {} | ||||
|     IResultCapture::~IResultCapture() {} | ||||
| @@ -77,6 +85,7 @@ namespace Catch { | ||||
|     FreeFunctionTestCase::~FreeFunctionTestCase() {} | ||||
|     IGeneratorInfo::~IGeneratorInfo() {} | ||||
|     IGeneratorsForTest::~IGeneratorsForTest() {} | ||||
|     WildcardPattern::~WildcardPattern() {} | ||||
|     TestSpec::Pattern::~Pattern() {} | ||||
|     TestSpec::NamePattern::~NamePattern() {} | ||||
|     TestSpec::TagPattern::~TagPattern() {} | ||||
| @@ -88,6 +97,13 @@ namespace Catch { | ||||
|     Matchers::Impl::StdString::EndsWith::~EndsWith() {} | ||||
|  | ||||
|     void Config::dummy() {} | ||||
|  | ||||
|     namespace TestCaseTracking { | ||||
|         ITracker::~ITracker() {} | ||||
|         TrackerBase::~TrackerBase() {} | ||||
|         SectionTracker::~SectionTracker() {} | ||||
|         IndexTracker::~IndexTracker() {} | ||||
|     } | ||||
| } | ||||
|  | ||||
| #ifdef __clang__ | ||||
|   | ||||
| @@ -18,6 +18,7 @@ namespace Catch { | ||||
|     class AssertionResult; | ||||
|     struct AssertionInfo; | ||||
|     struct SectionInfo; | ||||
|     struct SectionEndInfo; | ||||
|     struct MessageInfo; | ||||
|     class ScopedMessageBuilder; | ||||
|     struct Counts; | ||||
| @@ -29,7 +30,8 @@ namespace Catch { | ||||
|         virtual void assertionEnded( AssertionResult const& result ) = 0; | ||||
|         virtual bool sectionStarted(    SectionInfo const& sectionInfo, | ||||
|                                         Counts& assertions ) = 0; | ||||
|         virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; | ||||
|         virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; | ||||
|         virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; | ||||
|         virtual void pushScopedMessage( MessageInfo const& message ) = 0; | ||||
|         virtual void popScopedMessage( MessageInfo const& message ) = 0; | ||||
|  | ||||
|   | ||||
| @@ -9,15 +9,20 @@ | ||||
| #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include "catch_interfaces_registry_hub.h" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     typedef std::string(*exceptionTranslateFunction)(); | ||||
|  | ||||
|     struct IExceptionTranslator; | ||||
|     typedef std::vector<const IExceptionTranslator*> ExceptionTranslators; | ||||
|      | ||||
|     struct IExceptionTranslator { | ||||
|         virtual ~IExceptionTranslator(); | ||||
|         virtual std::string translate() const = 0; | ||||
|         virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; | ||||
|     }; | ||||
|  | ||||
|     struct IExceptionTranslatorRegistry { | ||||
| @@ -35,9 +40,12 @@ namespace Catch { | ||||
|             : m_translateFunction( translateFunction ) | ||||
|             {} | ||||
|  | ||||
|             virtual std::string translate() const { | ||||
|             virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { | ||||
|                 try { | ||||
|                     throw; | ||||
|                     if( it == itEnd ) | ||||
|                         throw; | ||||
|                     else | ||||
|                         return (*it)->translate( it+1, itEnd ); | ||||
|                 } | ||||
|                 catch( T& ex ) { | ||||
|                     return m_translateFunction( ex ); | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED | ||||
|  | ||||
| #include "catch_ptr.hpp" | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| namespace Catch { | ||||
| @@ -29,7 +31,8 @@ namespace Catch { | ||||
|  | ||||
|     struct IMutableRegistryHub { | ||||
|         virtual ~IMutableRegistryHub(); | ||||
|         virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; | ||||
|         virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0; | ||||
|         virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0; | ||||
|         virtual void registerTest( TestCase const& testInfo ) = 0; | ||||
|         virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; | ||||
|     }; | ||||
|   | ||||
| @@ -26,18 +26,18 @@ | ||||
| namespace Catch | ||||
| { | ||||
|     struct ReporterConfig { | ||||
|         explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) | ||||
|         explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig ) | ||||
|         :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} | ||||
|  | ||||
|         ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream ) | ||||
|         ReporterConfig( Ptr<IConfig const> 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; } | ||||
|         Ptr<IConfig const> fullConfig() const { return m_fullConfig; } | ||||
|  | ||||
|     private: | ||||
|         std::ostream* m_stream; | ||||
|         Ptr<IConfig> m_fullConfig; | ||||
|         Ptr<IConfig const> m_fullConfig; | ||||
|     }; | ||||
|  | ||||
|     struct ReporterPreferences { | ||||
| @@ -240,29 +240,34 @@ namespace Catch | ||||
|  | ||||
|         // The return value indicates if the messages buffer should be cleared: | ||||
|         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; | ||||
|          | ||||
|  | ||||
|         virtual void skipTest( TestCaseInfo const& testInfo ) = 0; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     struct IReporterFactory { | ||||
|     struct IReporterFactory : IShared { | ||||
|         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; | ||||
|         typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap; | ||||
|         typedef std::vector<Ptr<IReporterFactory> > Listeners; | ||||
|  | ||||
|         virtual ~IReporterRegistry(); | ||||
|         virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0; | ||||
|         virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0; | ||||
|         virtual FactoryMap const& getFactories() const = 0; | ||||
|         virtual Listeners const& getListeners() const = 0; | ||||
|     }; | ||||
|  | ||||
|     Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ); | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED | ||||
|   | ||||
| @@ -28,9 +28,13 @@ namespace Catch { | ||||
|     struct ITestCaseRegistry { | ||||
|         virtual ~ITestCaseRegistry(); | ||||
|         virtual std::vector<TestCase> const& getAllTests() const = 0; | ||||
|         virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0; | ||||
|  | ||||
|         virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0; | ||||
|     }; | ||||
|  | ||||
|     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); | ||||
|     std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); | ||||
|     std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED | ||||
|   | ||||
| @@ -34,8 +34,7 @@ namespace Catch { | ||||
|         nameAttr.setInitialIndent( 2 ).setIndent( 4 ); | ||||
|         tagsAttr.setIndent( 6 ); | ||||
|  | ||||
|         std::vector<TestCase> matchedTestCases; | ||||
|         getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); | ||||
|         std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); | ||||
|         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); | ||||
|                 it != itEnd; | ||||
|                 ++it ) { | ||||
| @@ -63,8 +62,7 @@ namespace Catch { | ||||
|         if( !config.testSpec().hasFilters() ) | ||||
|             testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); | ||||
|         std::size_t matchedTests = 0; | ||||
|         std::vector<TestCase> matchedTestCases; | ||||
|         getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); | ||||
|         std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); | ||||
|         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); | ||||
|                 it != itEnd; | ||||
|                 ++it ) { | ||||
| @@ -104,8 +102,7 @@ namespace Catch { | ||||
|  | ||||
|         std::map<std::string, TagInfo> tagCounts; | ||||
|  | ||||
|         std::vector<TestCase> matchedTestCases; | ||||
|         getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases ); | ||||
|         std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); | ||||
|         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); | ||||
|                 it != itEnd; | ||||
|                 ++it ) { | ||||
|   | ||||
| @@ -12,6 +12,12 @@ namespace Catch { | ||||
| namespace Matchers { | ||||
|     namespace Impl { | ||||
|  | ||||
|     namespace Generic { | ||||
|         template<typename ExpressionT> class AllOf; | ||||
|         template<typename ExpressionT> class AnyOf; | ||||
|         template<typename ExpressionT> class Not; | ||||
|     } | ||||
|          | ||||
|     template<typename ExpressionT> | ||||
|     struct Matcher : SharedImpl<IShared> | ||||
|     { | ||||
| @@ -21,6 +27,10 @@ namespace Matchers { | ||||
|         virtual Ptr<Matcher> clone() const = 0; | ||||
|         virtual bool match( ExpressionT const& expr ) const = 0; | ||||
|         virtual std::string toString() const = 0; | ||||
|          | ||||
|         Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const; | ||||
|         Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const; | ||||
|         Generic::Not<ExpressionT> operator ! () const; | ||||
|     }; | ||||
|  | ||||
|     template<typename DerivedT, typename ExpressionT> | ||||
| @@ -32,6 +42,22 @@ namespace Matchers { | ||||
|     }; | ||||
|  | ||||
|     namespace Generic { | ||||
|         template<typename ExpressionT> | ||||
|         class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> { | ||||
|         public: | ||||
|             explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {} | ||||
|             Not( Not const& other ) : m_matcher( other.m_matcher ) {} | ||||
|  | ||||
|             virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { | ||||
|                 return !m_matcher->match( expr ); | ||||
|             } | ||||
|  | ||||
|             virtual std::string toString() const CATCH_OVERRIDE { | ||||
|                 return "not " + m_matcher->toString(); | ||||
|             } | ||||
|         private: | ||||
|             Ptr< Matcher<ExpressionT> > m_matcher; | ||||
|         }; | ||||
|  | ||||
|         template<typename ExpressionT> | ||||
|         class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> { | ||||
| @@ -63,6 +89,12 @@ namespace Matchers { | ||||
|                 return oss.str(); | ||||
|             } | ||||
|  | ||||
|             AllOf operator && ( Matcher<ExpressionT> const& other ) const { | ||||
|                 AllOf allOfExpr( *this ); | ||||
|                 allOfExpr.add( other ); | ||||
|                 return allOfExpr; | ||||
|             } | ||||
|  | ||||
|         private: | ||||
|             std::vector<Ptr<Matcher<ExpressionT> > > m_matchers; | ||||
|         }; | ||||
| @@ -97,85 +129,146 @@ namespace Matchers { | ||||
|                 return oss.str(); | ||||
|             } | ||||
|  | ||||
|             AnyOf operator || ( Matcher<ExpressionT> const& other ) const { | ||||
|                 AnyOf anyOfExpr( *this ); | ||||
|                 anyOfExpr.add( other ); | ||||
|                 return anyOfExpr; | ||||
|             } | ||||
|              | ||||
|         private: | ||||
|             std::vector<Ptr<Matcher<ExpressionT> > > m_matchers; | ||||
|         }; | ||||
|  | ||||
|     } // namespace Generic | ||||
|          | ||||
|     template<typename ExpressionT> | ||||
|     Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const { | ||||
|         Generic::AllOf<ExpressionT> allOfExpr; | ||||
|         allOfExpr.add( *this ); | ||||
|         allOfExpr.add( other ); | ||||
|         return allOfExpr; | ||||
|     } | ||||
|  | ||||
|     template<typename ExpressionT> | ||||
|     Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const { | ||||
|         Generic::AnyOf<ExpressionT> anyOfExpr; | ||||
|         anyOfExpr.add( *this ); | ||||
|         anyOfExpr.add( other ); | ||||
|         return anyOfExpr; | ||||
|     } | ||||
|  | ||||
|     template<typename ExpressionT> | ||||
|     Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const { | ||||
|         return Generic::Not<ExpressionT>( *this ); | ||||
|     } | ||||
|          | ||||
|  | ||||
|     namespace StdString { | ||||
|  | ||||
|         inline std::string makeString( std::string const& str ) { return str; } | ||||
|         inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } | ||||
|  | ||||
|         struct CasedString | ||||
|         { | ||||
|             CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) | ||||
|             :   m_caseSensitivity( caseSensitivity ), | ||||
|                 m_str( adjustString( str ) ) | ||||
|             {} | ||||
|             std::string adjustString( std::string const& str ) const { | ||||
|                 return m_caseSensitivity == CaseSensitive::No | ||||
|                     ? toLower( str ) | ||||
|                     : str; | ||||
|  | ||||
|             } | ||||
|             std::string toStringSuffix() const | ||||
|             { | ||||
|                 return m_caseSensitivity == CaseSensitive::No | ||||
|                     ? " (case insensitive)" | ||||
|                     : ""; | ||||
|             } | ||||
|             CaseSensitive::Choice m_caseSensitivity; | ||||
|             std::string m_str; | ||||
|         }; | ||||
|  | ||||
|         struct Equals : MatcherImpl<Equals, std::string> { | ||||
|             Equals( std::string const& str ) : m_str( str ){} | ||||
|             Equals( Equals const& other ) : m_str( other.m_str ){} | ||||
|             Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) | ||||
|             :   m_data( str, caseSensitivity ) | ||||
|             {} | ||||
|             Equals( Equals const& other ) : m_data( other.m_data ){} | ||||
|  | ||||
|             virtual ~Equals(); | ||||
|  | ||||
|             virtual bool match( std::string const& expr ) const { | ||||
|                 return m_str == expr; | ||||
|                 return m_data.m_str == m_data.adjustString( expr );; | ||||
|             } | ||||
|             virtual std::string toString() const { | ||||
|                 return "equals: \"" + m_str + "\""; | ||||
|                 return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); | ||||
|             } | ||||
|  | ||||
|             std::string m_str; | ||||
|             CasedString m_data; | ||||
|         }; | ||||
|  | ||||
|         struct Contains : MatcherImpl<Contains, std::string> { | ||||
|             Contains( std::string const& substr ) : m_substr( substr ){} | ||||
|             Contains( Contains const& other ) : m_substr( other.m_substr ){} | ||||
|             Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) | ||||
|             : m_data( substr, caseSensitivity ){} | ||||
|             Contains( Contains const& other ) : m_data( other.m_data ){} | ||||
|  | ||||
|             virtual ~Contains(); | ||||
|  | ||||
|             virtual bool match( std::string const& expr ) const { | ||||
|                 return expr.find( m_substr ) != std::string::npos; | ||||
|                 return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; | ||||
|             } | ||||
|             virtual std::string toString() const { | ||||
|                 return "contains: \"" + m_substr + "\""; | ||||
|                 return "contains: \"" + m_data.m_str  + "\"" + m_data.toStringSuffix(); | ||||
|             } | ||||
|  | ||||
|             std::string m_substr; | ||||
|             CasedString m_data; | ||||
|         }; | ||||
|  | ||||
|         struct StartsWith : MatcherImpl<StartsWith, std::string> { | ||||
|             StartsWith( std::string const& substr ) : m_substr( substr ){} | ||||
|             StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} | ||||
|             StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) | ||||
|             : m_data( substr, caseSensitivity ){} | ||||
|  | ||||
|             StartsWith( StartsWith const& other ) : m_data( other.m_data ){} | ||||
|  | ||||
|             virtual ~StartsWith(); | ||||
|  | ||||
|             virtual bool match( std::string const& expr ) const { | ||||
|                 return expr.find( m_substr ) == 0; | ||||
|                 return m_data.adjustString( expr ).find( m_data.m_str ) == 0; | ||||
|             } | ||||
|             virtual std::string toString() const { | ||||
|                 return "starts with: \"" + m_substr + "\""; | ||||
|                 return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); | ||||
|             } | ||||
|  | ||||
|             std::string m_substr; | ||||
|             CasedString m_data; | ||||
|         }; | ||||
|  | ||||
|         struct EndsWith : MatcherImpl<EndsWith, std::string> { | ||||
|             EndsWith( std::string const& substr ) : m_substr( substr ){} | ||||
|             EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} | ||||
|             EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) | ||||
|             : m_data( substr, caseSensitivity ){} | ||||
|             EndsWith( EndsWith const& other ) : m_data( other.m_data ){} | ||||
|  | ||||
|             virtual ~EndsWith(); | ||||
|  | ||||
|             virtual bool match( std::string const& expr ) const { | ||||
|                 return expr.find( m_substr ) == expr.size() - m_substr.size(); | ||||
|                 return m_data.adjustString( expr ).find( m_data.m_str ) == expr.size() - m_data.m_str.size(); | ||||
|             } | ||||
|             virtual std::string toString() const { | ||||
|                 return "ends with: \"" + m_substr + "\""; | ||||
|                 return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); | ||||
|             } | ||||
|  | ||||
|             std::string m_substr; | ||||
|             CasedString m_data; | ||||
|         }; | ||||
|     } // namespace StdString | ||||
|     } // namespace Impl | ||||
|  | ||||
|     // The following functions create the actual matcher objects. | ||||
|     // This allows the types to be inferred | ||||
|     template<typename ExpressionT> | ||||
|     inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) { | ||||
|         return Impl::Generic::Not<ExpressionT>( m ); | ||||
|     } | ||||
|  | ||||
|     template<typename ExpressionT> | ||||
|     inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1, | ||||
|                                                     Impl::Matcher<ExpressionT> const& m2 ) { | ||||
| @@ -199,17 +292,17 @@ namespace Matchers { | ||||
|         return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 ); | ||||
|     } | ||||
|  | ||||
|     inline Impl::StdString::Equals      Equals( std::string const& str ) { | ||||
|         return Impl::StdString::Equals( str ); | ||||
|     inline Impl::StdString::Equals      Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { | ||||
|         return Impl::StdString::Equals( str, caseSensitivity ); | ||||
|     } | ||||
|     inline Impl::StdString::Equals      Equals( const char* str ) { | ||||
|         return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); | ||||
|     inline Impl::StdString::Equals      Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { | ||||
|         return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); | ||||
|     } | ||||
|     inline Impl::StdString::Contains    Contains( std::string const& substr ) { | ||||
|         return Impl::StdString::Contains( substr ); | ||||
|     inline Impl::StdString::Contains    Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { | ||||
|         return Impl::StdString::Contains( substr, caseSensitivity ); | ||||
|     } | ||||
|     inline Impl::StdString::Contains    Contains( const char* substr ) { | ||||
|         return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); | ||||
|     inline Impl::StdString::Contains    Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { | ||||
|         return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); | ||||
|     } | ||||
|     inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) { | ||||
|         return Impl::StdString::StartsWith( substr ); | ||||
|   | ||||
| @@ -72,7 +72,7 @@ namespace Catch { | ||||
|  | ||||
|     inline size_t registerTestMethods() { | ||||
|         size_t noTestMethods = 0; | ||||
|         int noClasses = objc_getClassList( NULL, 0 ); | ||||
|         int noClasses = objc_getClassList( CATCH_NULL, 0 ); | ||||
|  | ||||
|         Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); | ||||
|         objc_getClassList( classes, noClasses ); | ||||
|   | ||||
| @@ -16,12 +16,12 @@ namespace Catch { | ||||
|     template<typename T> | ||||
|     class Option { | ||||
|     public: | ||||
|         Option() : nullableValue( NULL ) {} | ||||
|         Option() : nullableValue( CATCH_NULL ) {} | ||||
|         Option( T const& _value ) | ||||
|         : nullableValue( new( storage ) T( _value ) ) | ||||
|         {} | ||||
|         Option( Option const& _other ) | ||||
|         : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) | ||||
|         : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) | ||||
|         {} | ||||
|  | ||||
|         ~Option() { | ||||
| @@ -45,7 +45,7 @@ namespace Catch { | ||||
|         void reset() { | ||||
|             if( nullableValue ) | ||||
|                 nullableValue->~T(); | ||||
|             nullableValue = NULL; | ||||
|             nullableValue = CATCH_NULL; | ||||
|         } | ||||
|  | ||||
|         T& operator*() { return *nullableValue; } | ||||
| @@ -57,10 +57,10 @@ namespace Catch { | ||||
|             return nullableValue ? *nullableValue : defaultValue; | ||||
|         } | ||||
|  | ||||
|         bool some() const { return nullableValue != NULL; } | ||||
|         bool none() const { return nullableValue == NULL; } | ||||
|         bool some() const { return nullableValue != CATCH_NULL; } | ||||
|         bool none() const { return nullableValue == CATCH_NULL; } | ||||
|  | ||||
|         bool operator !() const { return nullableValue == NULL; } | ||||
|         bool operator !() const { return nullableValue == CATCH_NULL; } | ||||
|         operator SafeBool::type() const { | ||||
|             return SafeBool::makeSafe( some() ); | ||||
|         } | ||||
|   | ||||
| @@ -23,7 +23,7 @@ namespace Catch { | ||||
|     template<typename T> | ||||
|     class Ptr { | ||||
|     public: | ||||
|         Ptr() : m_p( NULL ){} | ||||
|         Ptr() : m_p( CATCH_NULL ){} | ||||
|         Ptr( T* p ) : m_p( p ){ | ||||
|             if( m_p ) | ||||
|                 m_p->addRef(); | ||||
| @@ -39,7 +39,7 @@ namespace Catch { | ||||
|         void reset() { | ||||
|             if( m_p ) | ||||
|                 m_p->release(); | ||||
|             m_p = NULL; | ||||
|             m_p = CATCH_NULL; | ||||
|         } | ||||
|         Ptr& operator = ( T* p ){ | ||||
|             Ptr temp( p ); | ||||
| @@ -52,12 +52,11 @@ namespace Catch { | ||||
|             return *this; | ||||
|         } | ||||
|         void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } | ||||
|         T* get() { return m_p; } | ||||
|         const T* get() const{ return m_p; } | ||||
|         T* get() const{ return m_p; } | ||||
|         T& operator*() const { return *m_p; } | ||||
|         T* operator->() const { return m_p; } | ||||
|         bool operator !() const { return m_p == NULL; } | ||||
|         operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } | ||||
|         bool operator !() const { return m_p == CATCH_NULL; } | ||||
|         operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } | ||||
|  | ||||
|     private: | ||||
|         T* m_p; | ||||
|   | ||||
| @@ -26,24 +26,27 @@ namespace Catch { | ||||
|         public: // IRegistryHub | ||||
|             RegistryHub() { | ||||
|             } | ||||
|             virtual IReporterRegistry const& getReporterRegistry() const { | ||||
|             virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { | ||||
|                 return m_reporterRegistry; | ||||
|             } | ||||
|             virtual ITestCaseRegistry const& getTestCaseRegistry() const { | ||||
|             virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { | ||||
|                 return m_testCaseRegistry; | ||||
|             } | ||||
|             virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { | ||||
|             virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { | ||||
|                 return m_exceptionTranslatorRegistry; | ||||
|             } | ||||
|  | ||||
|         public: // IMutableRegistryHub | ||||
|             virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { | ||||
|             virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE { | ||||
|                 m_reporterRegistry.registerReporter( name, factory ); | ||||
|             } | ||||
|             virtual void registerTest( TestCase const& testInfo ) { | ||||
|             virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE { | ||||
|                 m_reporterRegistry.registerListener( factory ); | ||||
|             } | ||||
|             virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { | ||||
|                 m_testCaseRegistry.registerTest( testInfo ); | ||||
|             } | ||||
|             virtual void registerTranslator( const IExceptionTranslator* translator ) { | ||||
|             virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { | ||||
|                 m_exceptionTranslatorRegistry.registerTranslator( translator ); | ||||
|             } | ||||
|  | ||||
| @@ -55,7 +58,7 @@ namespace Catch { | ||||
|  | ||||
|         // Single, global, instance | ||||
|         inline RegistryHub*& getTheRegistryHub() { | ||||
|             static RegistryHub* theRegistryHub = NULL; | ||||
|             static RegistryHub* theRegistryHub = CATCH_NULL; | ||||
|             if( !theRegistryHub ) | ||||
|                 theRegistryHub = new RegistryHub(); | ||||
|             return theRegistryHub; | ||||
| @@ -70,7 +73,7 @@ namespace Catch { | ||||
|     } | ||||
|     void cleanUp() { | ||||
|         delete getTheRegistryHub(); | ||||
|         getTheRegistryHub() = NULL; | ||||
|         getTheRegistryHub() = CATCH_NULL; | ||||
|         cleanUpContext(); | ||||
|     } | ||||
|     std::string translateActiveException() { | ||||
|   | ||||
| @@ -36,7 +36,7 @@ namespace Catch { | ||||
|     template<typename T> | ||||
|     class ReporterRegistrar { | ||||
|  | ||||
|         class ReporterFactory : public IReporterFactory { | ||||
|         class ReporterFactory : public SharedImpl<IReporterFactory> { | ||||
|  | ||||
|             // *** Please Note ***: | ||||
|             // - If you end up here looking at a compiler error because it's trying to register | ||||
| @@ -64,11 +64,35 @@ namespace Catch { | ||||
|             getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     class ListenerRegistrar { | ||||
|  | ||||
|         class ListenerFactory : public SharedImpl<IReporterFactory> { | ||||
|  | ||||
|             virtual IStreamingReporter* create( ReporterConfig const& config ) const { | ||||
|                 return new T( config ); | ||||
|             } | ||||
|             virtual std::string getDescription() const { | ||||
|                 return ""; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     public: | ||||
|  | ||||
|         ListenerRegistrar() { | ||||
|             getMutableRegistryHub().registerListener( new ListenerFactory() ); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ | ||||
|     namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } | ||||
|  | ||||
| #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ | ||||
|     namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } | ||||
|  | ||||
| #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ | ||||
|     namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED | ||||
|   | ||||
| @@ -18,27 +18,32 @@ namespace Catch { | ||||
|  | ||||
|     public: | ||||
|  | ||||
|         virtual ~ReporterRegistry() { | ||||
|             deleteAllValues( m_factories ); | ||||
|         } | ||||
|         virtual ~ReporterRegistry() CATCH_OVERRIDE {} | ||||
|  | ||||
|         virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const { | ||||
|         virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE { | ||||
|             FactoryMap::const_iterator it =  m_factories.find( name ); | ||||
|             if( it == m_factories.end() ) | ||||
|                 return NULL; | ||||
|                 return CATCH_NULL; | ||||
|             return it->second->create( ReporterConfig( config ) ); | ||||
|         } | ||||
|  | ||||
|         void registerReporter( std::string const& name, IReporterFactory* factory ) { | ||||
|         void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) { | ||||
|             m_factories.insert( std::make_pair( name, factory ) ); | ||||
|         } | ||||
|         void registerListener( Ptr<IReporterFactory> const& factory ) { | ||||
|             m_listeners.push_back( factory ); | ||||
|         } | ||||
|  | ||||
|         FactoryMap const& getFactories() const { | ||||
|         virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { | ||||
|             return m_factories; | ||||
|         } | ||||
|         virtual Listeners const& getListeners() const CATCH_OVERRIDE { | ||||
|             return m_listeners; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         FactoryMap m_factories; | ||||
|         Listeners m_listeners; | ||||
|     }; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| #include "catch_result_type.h" | ||||
| #include "catch_assertionresult.h" | ||||
| #include "catch_common.h" | ||||
| #include "catch_matchers.hpp" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
| @@ -38,7 +39,8 @@ namespace Catch { | ||||
|         ResultBuilder(  char const* macroName, | ||||
|                         SourceLineInfo const& lineInfo, | ||||
|                         char const* capturedExpression, | ||||
|                         ResultDisposition::Flags resultDisposition ); | ||||
|                         ResultDisposition::Flags resultDisposition, | ||||
|                         char const* secondArg = "" ); | ||||
|  | ||||
|         template<typename T> | ||||
|         ExpressionLhs<T const&> operator <= ( T const& operand ); | ||||
| @@ -67,6 +69,9 @@ namespace Catch { | ||||
|         void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); | ||||
|         void captureResult( ResultWas::OfType resultType ); | ||||
|         void captureExpression(); | ||||
|         void captureExpectedException( std::string const& expectedMessage ); | ||||
|         void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ); | ||||
|         void handleResult( AssertionResult const& result ); | ||||
|         void react(); | ||||
|         bool shouldDebugBreak() const; | ||||
|         bool allowThrows() const; | ||||
| @@ -80,7 +85,7 @@ namespace Catch { | ||||
|             std::string lhs, rhs, op; | ||||
|         } m_exprComponents; | ||||
|         CopyableStream m_stream; | ||||
|          | ||||
|  | ||||
|         bool m_shouldDebugBreak; | ||||
|         bool m_shouldThrow; | ||||
|     }; | ||||
|   | ||||
| @@ -14,15 +14,21 @@ | ||||
| #include "catch_interfaces_runner.h" | ||||
| #include "catch_interfaces_capture.h" | ||||
| #include "catch_interfaces_registry_hub.h" | ||||
|  | ||||
| #include "catch_wildcard_pattern.hpp" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { | ||||
|         return secondArg.empty() || secondArg == "\"\"" | ||||
|             ? capturedExpression | ||||
|             : capturedExpression + ", " + secondArg; | ||||
|     } | ||||
|     ResultBuilder::ResultBuilder(   char const* macroName, | ||||
|                                     SourceLineInfo const& lineInfo, | ||||
|                                     char const* capturedExpression, | ||||
|                                     ResultDisposition::Flags resultDisposition ) | ||||
|     :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ), | ||||
|                                     ResultDisposition::Flags resultDisposition, | ||||
|                                     char const* secondArg ) | ||||
|     :   m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), | ||||
|         m_shouldDebugBreak( false ), | ||||
|         m_shouldThrow( false ) | ||||
|     {} | ||||
| @@ -63,9 +69,35 @@ namespace Catch { | ||||
|         setResultType( resultType ); | ||||
|         captureExpression(); | ||||
|     } | ||||
|     void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { | ||||
|         if( expectedMessage.empty() ) | ||||
|             captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() ); | ||||
|         else | ||||
|             captureExpectedException( Matchers::Equals( expectedMessage ) ); | ||||
|     } | ||||
|  | ||||
|     void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) { | ||||
|  | ||||
|         assert( m_exprComponents.testFalse == false ); | ||||
|         AssertionResultData data = m_data; | ||||
|         data.resultType = ResultWas::Ok; | ||||
|         data.reconstructedExpression = m_assertionInfo.capturedExpression; | ||||
|  | ||||
|         std::string actualMessage = Catch::translateActiveException(); | ||||
|         if( !matcher.match( actualMessage ) ) { | ||||
|             data.resultType = ResultWas::ExpressionFailed; | ||||
|             data.reconstructedExpression = actualMessage; | ||||
|         } | ||||
|         AssertionResult result( m_assertionInfo, data ); | ||||
|         handleResult( result ); | ||||
|     } | ||||
|  | ||||
|     void ResultBuilder::captureExpression() { | ||||
|         AssertionResult result = build(); | ||||
|         handleResult( result ); | ||||
|     } | ||||
|     void ResultBuilder::handleResult( AssertionResult const& result ) | ||||
|     { | ||||
|         getResultCapture().assertionEnded( result ); | ||||
|  | ||||
|         if( !result.isOk() ) { | ||||
|   | ||||
| @@ -55,7 +55,7 @@ namespace Catch { | ||||
|     inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } | ||||
|     inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; } | ||||
|     inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; } | ||||
|      | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED | ||||
|   | ||||
| @@ -59,15 +59,12 @@ namespace Catch { | ||||
| 
 | ||||
|     public: | ||||
| 
 | ||||
|         explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter ) | ||||
|         :   m_runInfo( config->name() ), | ||||
|         explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter ) | ||||
|         :   m_runInfo( _config->name() ), | ||||
|             m_context( getCurrentMutableContext() ), | ||||
|             m_activeTestCase( NULL ), | ||||
|             m_config( config ), | ||||
|             m_reporter( reporter ), | ||||
|             m_prevRunner( m_context.getRunner() ), | ||||
|             m_prevResultCapture( m_context.getResultCapture() ), | ||||
|             m_prevConfig( m_context.getConfig() ) | ||||
|             m_activeTestCase( CATCH_NULL ), | ||||
|             m_config( _config ), | ||||
|             m_reporter( reporter ) | ||||
|         { | ||||
|             m_context.setRunner( this ); | ||||
|             m_context.setConfig( m_config ); | ||||
| @@ -77,10 +74,6 @@ namespace Catch { | ||||
| 
 | ||||
|         virtual ~RunContext() { | ||||
|             m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); | ||||
|             m_context.setRunner( m_prevRunner ); | ||||
|             m_context.setConfig( NULL ); | ||||
|             m_context.setResultCapture( m_prevResultCapture ); | ||||
|             m_context.setConfig( m_prevConfig ); | ||||
|         } | ||||
| 
 | ||||
|         void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { | ||||
| @@ -101,14 +94,18 @@ namespace Catch { | ||||
|             m_reporter->testCaseStarting( testInfo ); | ||||
| 
 | ||||
|             m_activeTestCase = &testCase; | ||||
|             m_testCaseTracker = TestCaseTracker( testInfo.name ); | ||||
| 
 | ||||
| 
 | ||||
|             do { | ||||
|                 m_trackerContext.startRun(); | ||||
|                 do { | ||||
|                     m_trackerContext.startCycle(); | ||||
|                     m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); | ||||
|                     runCurrentTest( redirectedCout, redirectedCerr ); | ||||
|                 } | ||||
|                 while( !m_testCaseTracker->isCompleted() && !aborting() ); | ||||
|                 while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); | ||||
|             } | ||||
|             // !TBD: deprecated - this will be replaced by indexed trackers
 | ||||
|             while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); | ||||
| 
 | ||||
|             Totals deltaTotals = m_totals.delta( prevTotals ); | ||||
| @@ -119,8 +116,8 @@ namespace Catch { | ||||
|                                                         redirectedCerr, | ||||
|                                                         aborting() ) ); | ||||
| 
 | ||||
|             m_activeTestCase = NULL; | ||||
|             m_testCaseTracker.reset(); | ||||
|             m_activeTestCase = CATCH_NULL; | ||||
|             m_testCaseTracker = CATCH_NULL; | ||||
| 
 | ||||
|             return deltaTotals; | ||||
|         } | ||||
| @@ -156,8 +153,10 @@ namespace Catch { | ||||
|             std::ostringstream oss; | ||||
|             oss << sectionInfo.name << "@" << sectionInfo.lineInfo; | ||||
| 
 | ||||
|             if( !m_testCaseTracker->enterSection( oss.str() ) ) | ||||
|             ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); | ||||
|             if( !sectionTracker.isOpen() ) | ||||
|                 return false; | ||||
|             m_activeSections.push_back( §ionTracker ); | ||||
| 
 | ||||
|             m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; | ||||
| 
 | ||||
| @@ -168,30 +167,40 @@ namespace Catch { | ||||
|             return true; | ||||
|         } | ||||
|         bool testForMissingAssertions( Counts& assertions ) { | ||||
|             if( assertions.total() != 0 || | ||||
|                     !m_config->warnAboutMissingAssertions() || | ||||
|                     m_testCaseTracker->currentSectionHasChildren() ) | ||||
|             if( assertions.total() != 0 ) | ||||
|                 return false; | ||||
|             if( !m_config->warnAboutMissingAssertions() ) | ||||
|                 return false; | ||||
|             if( m_trackerContext.currentTracker().hasChildren() ) | ||||
|                 return false; | ||||
|             m_totals.assertions.failed++; | ||||
|             assertions.failed++; | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { | ||||
|             if( std::uncaught_exception() ) { | ||||
|                 m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             Counts assertions = m_totals.assertions - prevAssertions; | ||||
|         virtual void sectionEnded( SectionEndInfo const& endInfo ) { | ||||
|             Counts assertions = m_totals.assertions - endInfo.prevAssertions; | ||||
|             bool missingAssertions = testForMissingAssertions( assertions ); | ||||
| 
 | ||||
|             m_testCaseTracker->leaveSection(); | ||||
|             if( !m_activeSections.empty() ) { | ||||
|                 m_activeSections.back()->close(); | ||||
|                 m_activeSections.pop_back(); | ||||
|             } | ||||
| 
 | ||||
|             m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); | ||||
|             m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); | ||||
|             m_messages.clear(); | ||||
|         } | ||||
| 
 | ||||
|         virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { | ||||
|             if( m_unfinishedSections.empty() ) | ||||
|                 m_activeSections.back()->fail(); | ||||
|             else | ||||
|                 m_activeSections.back()->close(); | ||||
|             m_activeSections.pop_back(); | ||||
| 
 | ||||
|             m_unfinishedSections.push_back( endInfo ); | ||||
|         } | ||||
| 
 | ||||
|         virtual void pushScopedMessage( MessageInfo const& message ) { | ||||
|             m_messages.push_back( message ); | ||||
|         } | ||||
| @@ -257,7 +266,8 @@ namespace Catch { | ||||
|             double duration = 0; | ||||
|             try { | ||||
|                 m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); | ||||
|                 TestCaseTracker::Guard guard( *m_testCaseTracker ); | ||||
| 
 | ||||
|                 seedRng( *m_config ); | ||||
| 
 | ||||
|                 Timer timer; | ||||
|                 timer.start(); | ||||
| @@ -277,6 +287,7 @@ namespace Catch { | ||||
|             catch(...) { | ||||
|                 makeUnexpectedResultBuilder().useActiveException(); | ||||
|             } | ||||
|             m_testCaseTracker->close(); | ||||
|             handleUnfinishedSections(); | ||||
|             m_messages.clear(); | ||||
| 
 | ||||
| @@ -311,39 +322,29 @@ namespace Catch { | ||||
|         void handleUnfinishedSections() { | ||||
|             // 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_reverse_iterator it = m_unfinishedSections.rbegin(), | ||||
|             for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(), | ||||
|                         itEnd = m_unfinishedSections.rend(); | ||||
|                     it != itEnd; | ||||
|                     ++it ) | ||||
|                 sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); | ||||
|                 sectionEnded( *it ); | ||||
|             m_unfinishedSections.clear(); | ||||
|         } | ||||
| 
 | ||||
|         struct UnfinishedSections { | ||||
|             UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) | ||||
|             : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) | ||||
|             {} | ||||
| 
 | ||||
|             SectionInfo info; | ||||
|             Counts prevAssertions; | ||||
|             double durationInSeconds; | ||||
|         }; | ||||
| 
 | ||||
|         TestRunInfo m_runInfo; | ||||
|         IMutableContext& m_context; | ||||
|         TestCase const* m_activeTestCase; | ||||
|         Option<TestCaseTracker> m_testCaseTracker; | ||||
|         ITracker* m_testCaseTracker; | ||||
|         ITracker* m_currentSectionTracker; | ||||
|         AssertionResult m_lastResult; | ||||
| 
 | ||||
|         Ptr<IConfig const> m_config; | ||||
|         Totals m_totals; | ||||
|         Ptr<IStreamingReporter> m_reporter; | ||||
|         std::vector<MessageInfo> m_messages; | ||||
|         IRunner* m_prevRunner; | ||||
|         IResultCapture* m_prevResultCapture; | ||||
|         Ptr<IConfig const> m_prevConfig; | ||||
|         AssertionInfo m_lastAssertionInfo; | ||||
|         std::vector<UnfinishedSections> m_unfinishedSections; | ||||
|         std::vector<SectionEndInfo> m_unfinishedSections; | ||||
|         std::vector<ITracker*> m_activeSections; | ||||
|         TrackerContext m_trackerContext; | ||||
|     }; | ||||
| 
 | ||||
|     IResultCapture& getResultCapture() { | ||||
| @@ -32,8 +32,13 @@ namespace Catch { | ||||
|     } | ||||
|  | ||||
|     Section::~Section() { | ||||
|         if( m_sectionIncluded ) | ||||
|             getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); | ||||
|         if( m_sectionIncluded ) { | ||||
|             SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); | ||||
|             if( std::uncaught_exception() ) | ||||
|                 getResultCapture().sectionEndedEarly( endInfo ); | ||||
|             else | ||||
|                 getResultCapture().sectionEnded( endInfo ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // This indicates whether the section should be executed or not | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED | ||||
|  | ||||
| #include "catch_common.h" | ||||
| #include "catch_totals.hpp" | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
| @@ -23,6 +24,16 @@ namespace Catch { | ||||
|         SourceLineInfo lineInfo; | ||||
|     }; | ||||
|  | ||||
|     struct SectionEndInfo { | ||||
|         SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) | ||||
|         : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) | ||||
|         {} | ||||
|  | ||||
|         SectionInfo sectionInfo; | ||||
|         Counts prevAssertions; | ||||
|         double durationInSeconds; | ||||
|     }; | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED | ||||
|   | ||||
| @@ -36,7 +36,7 @@ namespace Catch { | ||||
|  | ||||
|         RunningSection( std::string const& name ) | ||||
|         :   m_state( Root ), | ||||
|             m_parent( NULL ), | ||||
|             m_parent( CATCH_NULL ), | ||||
|             m_name( name ) | ||||
|         {} | ||||
|  | ||||
|   | ||||
| @@ -9,28 +9,55 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED | ||||
|  | ||||
| #include <streambuf> | ||||
| #include "catch_compiler_capabilities.h" | ||||
| #include "catch_streambuf.h" | ||||
|  | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic ignored "-Wpadded" | ||||
| #endif | ||||
| #include <streambuf> | ||||
| #include <ostream> | ||||
| #include <fstream> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     class Stream { | ||||
|     public: | ||||
|         Stream(); | ||||
|         Stream( std::streambuf* _streamBuf, bool _isOwned ); | ||||
|         void release(); | ||||
|  | ||||
|         std::streambuf* streamBuf; | ||||
|  | ||||
|     private: | ||||
|         bool isOwned; | ||||
|     }; | ||||
|  | ||||
|     std::ostream& cout(); | ||||
|     std::ostream& cerr(); | ||||
|  | ||||
|  | ||||
|     struct IStream { | ||||
|         virtual ~IStream() CATCH_NOEXCEPT; | ||||
|         virtual std::ostream& stream() const = 0; | ||||
|     }; | ||||
|  | ||||
|     class FileStream : public IStream { | ||||
|         mutable std::ofstream m_ofs; | ||||
|     public: | ||||
|         FileStream( std::string const& filename ); | ||||
|         virtual ~FileStream() CATCH_NOEXCEPT; | ||||
|     public: // IStream | ||||
|         virtual std::ostream& stream() const CATCH_OVERRIDE; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     class CoutStream : public IStream { | ||||
|         mutable std::ostream m_os; | ||||
|     public: | ||||
|         CoutStream(); | ||||
|         virtual ~CoutStream() CATCH_NOEXCEPT; | ||||
|  | ||||
|     public: // IStream | ||||
|         virtual std::ostream& stream() const CATCH_OVERRIDE; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     class DebugOutStream : public IStream { | ||||
|         std::auto_ptr<StreamBufBase> m_streamBuf; | ||||
|         mutable std::ostream m_os; | ||||
|     public: | ||||
|         DebugOutStream(); | ||||
|         virtual ~DebugOutStream() CATCH_NOEXCEPT; | ||||
|  | ||||
|     public: // IStream | ||||
|         virtual std::ostream& stream() const CATCH_OVERRIDE; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED | ||||
|   | ||||
| @@ -10,7 +10,6 @@ | ||||
| #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED | ||||
|  | ||||
| #include "catch_stream.h" | ||||
| #include "catch_streambuf.h" | ||||
| #include "catch_debugger.h" | ||||
|  | ||||
| #include <stdexcept> | ||||
| @@ -57,6 +56,20 @@ namespace Catch { | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|  | ||||
|     FileStream::FileStream( std::string const& filename ) { | ||||
|         m_ofs.open( filename.c_str() ); | ||||
|         if( m_ofs.fail() ) { | ||||
|             std::ostringstream oss; | ||||
|             oss << "Unable to open file: '" << filename << "'"; | ||||
|             throw std::domain_error( oss.str() ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::ostream& FileStream::stream() const { | ||||
|         return m_ofs; | ||||
|     } | ||||
|  | ||||
|     struct OutputDebugWriter { | ||||
|  | ||||
|         void operator()( std::string const&str ) { | ||||
| @@ -64,20 +77,23 @@ namespace Catch { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     Stream::Stream() | ||||
|     : streamBuf( NULL ), isOwned( false ) | ||||
|     DebugOutStream::DebugOutStream() | ||||
|     :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), | ||||
|         m_os( m_streamBuf.get() ) | ||||
|     {} | ||||
|  | ||||
|     Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) | ||||
|     : streamBuf( _streamBuf ), isOwned( _isOwned ) | ||||
|     std::ostream& DebugOutStream::stream() const { | ||||
|         return m_os; | ||||
|     } | ||||
|  | ||||
|     // Store the streambuf from cout up-front because | ||||
|     // cout may get redirected when running tests | ||||
|     CoutStream::CoutStream() | ||||
|     :   m_os( Catch::cout().rdbuf() ) | ||||
|     {} | ||||
|  | ||||
|     void Stream::release() { | ||||
|         if( isOwned ) { | ||||
|             delete streamBuf; | ||||
|             streamBuf = NULL; | ||||
|             isOwned = false; | ||||
|         } | ||||
|     std::ostream& CoutStream::stream() const { | ||||
|         return m_os; | ||||
|     } | ||||
|  | ||||
| #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions | ||||
|   | ||||
| @@ -5,9 +5,6 @@ | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
|  | ||||
| #ifndef TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED | ||||
|  | ||||
| #ifdef __clang__ | ||||
| #   ifdef __ICC // icpc defines the __clang__ macro | ||||
| #       pragma warning(push) | ||||
| @@ -22,6 +19,7 @@ | ||||
| #       pragma clang diagnostic ignored "-Wc++98-compat" | ||||
| #       pragma clang diagnostic ignored "-Wc++98-compat-pedantic" | ||||
| #       pragma clang diagnostic ignored "-Wswitch-enum" | ||||
| #       pragma clang diagnostic ignored "-Wcovered-switch-default" | ||||
| #    endif | ||||
| #elif defined __GNUC__ | ||||
| #    pragma GCC diagnostic ignored "-Wvariadic-macros" | ||||
| @@ -29,5 +27,3 @@ | ||||
| #    pragma GCC diagnostic push | ||||
| #    pragma GCC diagnostic ignored "-Wpadded" | ||||
| #endif | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED | ||||
|   | ||||
| @@ -31,7 +31,7 @@ namespace Catch { | ||||
|             MayFail = 1 << 3, | ||||
|             Throws = 1 << 4 | ||||
|         }; | ||||
|          | ||||
|  | ||||
|         TestCaseInfo(   std::string const& _name, | ||||
|                         std::string const& _className, | ||||
|                         std::string const& _description, | ||||
| @@ -40,6 +40,8 @@ namespace Catch { | ||||
|  | ||||
|         TestCaseInfo( TestCaseInfo const& other ); | ||||
|  | ||||
|         friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags ); | ||||
|  | ||||
|         bool isHidden() const; | ||||
|         bool throws() const; | ||||
|         bool okToFail() const; | ||||
|   | ||||
| @@ -93,6 +93,21 @@ namespace Catch { | ||||
|         return TestCase( _testCase, info ); | ||||
|     } | ||||
|  | ||||
|     void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags ) | ||||
|     { | ||||
|         testCaseInfo.tags = tags; | ||||
|         testCaseInfo.lcaseTags.clear(); | ||||
|  | ||||
|         std::ostringstream oss; | ||||
|         for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { | ||||
|             oss << "[" << *it << "]"; | ||||
|             std::string lcaseTag = toLower( *it ); | ||||
|             testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); | ||||
|             testCaseInfo.lcaseTags.insert( lcaseTag ); | ||||
|         } | ||||
|         testCaseInfo.tagsAsString = oss.str(); | ||||
|     } | ||||
|  | ||||
|     TestCaseInfo::TestCaseInfo( std::string const& _name, | ||||
|                                 std::string const& _className, | ||||
|                                 std::string const& _description, | ||||
| @@ -101,18 +116,10 @@ namespace Catch { | ||||
|     :   name( _name ), | ||||
|         className( _className ), | ||||
|         description( _description ), | ||||
|         tags( _tags ), | ||||
|         lineInfo( _lineInfo ), | ||||
|         properties( None ) | ||||
|     { | ||||
|         std::ostringstream oss; | ||||
|         for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) { | ||||
|             oss << "[" << *it << "]"; | ||||
|             std::string lcaseTag = toLower( *it ); | ||||
|             properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) ); | ||||
|             lcaseTags.insert( lcaseTag ); | ||||
|         } | ||||
|         tagsAsString = oss.str(); | ||||
|         setTags( *this, _tags ); | ||||
|     } | ||||
|  | ||||
|     TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) | ||||
|   | ||||
| @@ -21,14 +21,71 @@ | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     class TestRegistry : public ITestCaseRegistry { | ||||
|         struct LexSort { | ||||
|             bool operator() (TestCase i,TestCase j) const { return (i<j);} | ||||
|         }; | ||||
|         struct RandomNumberGenerator { | ||||
|             int operator()( int n ) const { return std::rand() % n; } | ||||
|         }; | ||||
|     struct LexSort { | ||||
|         bool operator() (TestCase i,TestCase j) const { return (i<j);} | ||||
|     }; | ||||
|     struct RandomNumberGenerator { | ||||
|         int operator()( int n ) const { return std::rand() % n; } | ||||
|     }; | ||||
|  | ||||
|     inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { | ||||
|  | ||||
|         std::vector<TestCase> sorted = unsortedTestCases; | ||||
|  | ||||
|         switch( config.runOrder() ) { | ||||
|             case RunTests::InLexicographicalOrder: | ||||
|                 std::sort( sorted.begin(), sorted.end(), LexSort() ); | ||||
|                 break; | ||||
|             case RunTests::InRandomOrder: | ||||
|                 { | ||||
|                     seedRng( config ); | ||||
|  | ||||
|                     RandomNumberGenerator rng; | ||||
|                     std::random_shuffle( sorted.begin(), sorted.end(), rng ); | ||||
|                 } | ||||
|                 break; | ||||
|             case RunTests::InDeclarationOrder: | ||||
|                 // already in declaration order | ||||
|                 break; | ||||
|         } | ||||
|         return sorted; | ||||
|     } | ||||
|     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { | ||||
|         return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); | ||||
|     } | ||||
|  | ||||
|     void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { | ||||
|         std::set<TestCase> seenFunctions; | ||||
|         for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end(); | ||||
|             it != itEnd; | ||||
|             ++it ) { | ||||
|             std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it ); | ||||
|             if( !prev.second ){ | ||||
|                 Catch::cerr() | ||||
|                 << Colour( Colour::Red ) | ||||
|                 << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" | ||||
|                 << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" | ||||
|                 << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; | ||||
|                 exit(1); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { | ||||
|         std::vector<TestCase> filtered; | ||||
|         filtered.reserve( testCases.size() ); | ||||
|         for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end(); | ||||
|                 it != itEnd; | ||||
|                 ++it ) | ||||
|             if( matchTest( *it, testSpec, config ) ) | ||||
|                 filtered.push_back( *it ); | ||||
|         return filtered; | ||||
|     } | ||||
|     std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { | ||||
|         return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); | ||||
|     } | ||||
|  | ||||
|     class TestRegistry : public ITestCaseRegistry { | ||||
|     public: | ||||
|         TestRegistry() : m_unnamedCount( 0 ) {} | ||||
|         virtual ~TestRegistry(); | ||||
| @@ -40,68 +97,27 @@ namespace Catch { | ||||
|                 oss << "Anonymous test case " << ++m_unnamedCount; | ||||
|                 return registerTest( testCase.withName( oss.str() ) ); | ||||
|             } | ||||
|  | ||||
|             if( m_functions.find( testCase ) == m_functions.end() ) { | ||||
|                 m_functions.insert( testCase ); | ||||
|                 m_functionsInOrder.push_back( testCase ); | ||||
|                 if( !testCase.isHidden() ) | ||||
|                     m_nonHiddenFunctions.push_back( testCase ); | ||||
|             } | ||||
|             else { | ||||
|                 TestCase const& prev = *m_functions.find( testCase ); | ||||
|                 { | ||||
|                     Colour colourGuard( Colour::Red ); | ||||
|                     Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" | ||||
|                                 << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" | ||||
|                                 << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; | ||||
|                 } | ||||
|                 exit(1); | ||||
|             } | ||||
|             m_functions.push_back( testCase ); | ||||
|         } | ||||
|  | ||||
|         virtual std::vector<TestCase> const& getAllTests() const { | ||||
|             return m_functionsInOrder; | ||||
|             return m_functions; | ||||
|         } | ||||
|         virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const { | ||||
|             if( m_sortedFunctions.empty() ) | ||||
|                 enforceNoDuplicateTestCases( m_functions ); | ||||
|  | ||||
|         virtual std::vector<TestCase> const& getAllNonHiddenTests() const { | ||||
|             return m_nonHiddenFunctions; | ||||
|         } | ||||
|  | ||||
|         virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const { | ||||
|  | ||||
|             for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(), | ||||
|                                                         itEnd = m_functionsInOrder.end(); | ||||
|                     it != itEnd; | ||||
|                     ++it ) { | ||||
|                 bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() ); | ||||
|                 if( includeTest != negated ) | ||||
|                     matchingTestCases.push_back( *it ); | ||||
|             if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { | ||||
|                 m_sortedFunctions = sortTests( config, m_functions ); | ||||
|                 m_currentSortOrder = config.runOrder(); | ||||
|             } | ||||
|             sortTests( config, matchingTestCases ); | ||||
|             return m_sortedFunctions; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|  | ||||
|         static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) { | ||||
|              | ||||
|             switch( config.runOrder() ) { | ||||
|                 case RunTests::InLexicographicalOrder: | ||||
|                     std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() ); | ||||
|                     break; | ||||
|                 case RunTests::InRandomOrder: | ||||
|                 { | ||||
|                     RandomNumberGenerator rng; | ||||
|                     std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng ); | ||||
|                 } | ||||
|                     break; | ||||
|                 case RunTests::InDeclarationOrder: | ||||
|                     // already in declaration order | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         std::set<TestCase> m_functions; | ||||
|         std::vector<TestCase> m_functionsInOrder; | ||||
|         std::vector<TestCase> m_nonHiddenFunctions; | ||||
|         std::vector<TestCase> m_functions; | ||||
|         mutable RunTests::InWhatOrder m_currentSortOrder; | ||||
|         mutable std::vector<TestCase> m_sortedFunctions; | ||||
|         size_t m_unnamedCount; | ||||
|         std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised | ||||
|     }; | ||||
| @@ -136,29 +152,38 @@ namespace Catch { | ||||
|         return className; | ||||
|     } | ||||
|  | ||||
|     void registerTestCase | ||||
|         (   ITestCase* testCase, | ||||
|             char const* classOrQualifiedMethodName, | ||||
|             NameAndDesc const& nameAndDesc, | ||||
|             SourceLineInfo const& lineInfo ) { | ||||
|          | ||||
|         getMutableRegistryHub().registerTest | ||||
|             ( makeTestCase | ||||
|                 (   testCase, | ||||
|                     extractClassName( classOrQualifiedMethodName ), | ||||
|                     nameAndDesc.name, | ||||
|                     nameAndDesc.description, | ||||
|                     lineInfo ) ); | ||||
|     } | ||||
|     void registerTestCaseFunction | ||||
|         (   TestFunction function, | ||||
|             SourceLineInfo const& lineInfo, | ||||
|             NameAndDesc const& nameAndDesc ) { | ||||
|         registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); | ||||
|     } | ||||
|      | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|     AutoReg::AutoReg(   TestFunction function, | ||||
|                         SourceLineInfo const& lineInfo, | ||||
|                         NameAndDesc const& nameAndDesc ) { | ||||
|         registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); | ||||
|     AutoReg::AutoReg | ||||
|         (   TestFunction function, | ||||
|             SourceLineInfo const& lineInfo, | ||||
|             NameAndDesc const& nameAndDesc ) { | ||||
|         registerTestCaseFunction( function, lineInfo, nameAndDesc ); | ||||
|     } | ||||
|  | ||||
|     AutoReg::~AutoReg() {} | ||||
|  | ||||
|     void AutoReg::registerTestCase( ITestCase* testCase, | ||||
|                                     char const* classOrQualifiedMethodName, | ||||
|                                     NameAndDesc const& nameAndDesc, | ||||
|                                     SourceLineInfo const& lineInfo ) { | ||||
|  | ||||
|         getMutableRegistryHub().registerTest | ||||
|             ( makeTestCase( testCase, | ||||
|                             extractClassName( classOrQualifiedMethodName ), | ||||
|                             nameAndDesc.name, | ||||
|                             nameAndDesc.description, | ||||
|                             lineInfo ) ); | ||||
|     } | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,140 +8,308 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED | ||||
|  | ||||
| #include "catch_compiler_capabilities.h" | ||||
| #include "catch_ptr.hpp" | ||||
|  | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <assert.h> | ||||
| #include <vector> | ||||
|  | ||||
| namespace Catch { | ||||
| namespace SectionTracking { | ||||
| namespace TestCaseTracking { | ||||
|  | ||||
|     struct ITracker : SharedImpl<> { | ||||
|         virtual ~ITracker(); | ||||
|  | ||||
|         // static queries | ||||
|         virtual std::string name() const = 0; | ||||
|  | ||||
|         // dynamic queries | ||||
|         virtual bool isComplete() const = 0; // Successfully completed or failed | ||||
|         virtual bool isSuccessfullyCompleted() const = 0; | ||||
|         virtual bool isOpen() const = 0; // Started but not complete | ||||
|         virtual bool hasChildren() const = 0; | ||||
|  | ||||
|         virtual ITracker& parent() = 0; | ||||
|  | ||||
|         // actions | ||||
|         virtual void close() = 0; // Successfully complete | ||||
|         virtual void fail() = 0; | ||||
|         virtual void markAsNeedingAnotherRun() = 0; | ||||
|  | ||||
|         virtual void addChild( Ptr<ITracker> const& child ) = 0; | ||||
|         virtual ITracker* findChild( std::string const& name ) = 0; | ||||
|         virtual void openChild() = 0; | ||||
|     }; | ||||
|  | ||||
|     class TrackerContext { | ||||
|  | ||||
|     class TrackedSection { | ||||
|          | ||||
|         typedef std::map<std::string, TrackedSection> TrackedSections; | ||||
|          | ||||
|     public: | ||||
|         enum RunState { | ||||
|             NotStarted, | ||||
|             Executing, | ||||
|             ExecutingChildren, | ||||
|             Completed | ||||
|             CompletedCycle | ||||
|         }; | ||||
|          | ||||
|         TrackedSection( std::string const& name, TrackedSection* parent ) | ||||
|         :   m_name( name ), m_runState( NotStarted ), m_parent( parent ) | ||||
|  | ||||
|         Ptr<ITracker> m_rootTracker; | ||||
|         ITracker* m_currentTracker; | ||||
|         RunState m_runState; | ||||
|  | ||||
|     public: | ||||
|  | ||||
|         static TrackerContext& instance() { | ||||
|             static TrackerContext s_instance; | ||||
|             return s_instance; | ||||
|         } | ||||
|  | ||||
|         TrackerContext() | ||||
|         :   m_currentTracker( CATCH_NULL ), | ||||
|             m_runState( NotStarted ) | ||||
|         {} | ||||
|          | ||||
|         RunState runState() const { return m_runState; } | ||||
|          | ||||
|         TrackedSection* findChild( std::string const& childName ); | ||||
|         TrackedSection* acquireChild( std::string const& childName ); | ||||
|  | ||||
|         void enter() { | ||||
|             if( m_runState == NotStarted ) | ||||
|                 m_runState = Executing; | ||||
|         } | ||||
|         void leave(); | ||||
|  | ||||
|         TrackedSection* getParent() { | ||||
|             return m_parent; | ||||
|         ITracker& startRun(); | ||||
|  | ||||
|         void endRun() { | ||||
|             m_rootTracker.reset(); | ||||
|             m_currentTracker = CATCH_NULL; | ||||
|             m_runState = NotStarted; | ||||
|         } | ||||
|         bool hasChildren() const { | ||||
|  | ||||
|         void startCycle() { | ||||
|             m_currentTracker = m_rootTracker.get(); | ||||
|             m_runState = Executing; | ||||
|         } | ||||
|         void completeCycle() { | ||||
|             m_runState = CompletedCycle; | ||||
|         } | ||||
|  | ||||
|         bool completedCycle() const { | ||||
|             return m_runState == CompletedCycle; | ||||
|         } | ||||
|         ITracker& currentTracker() { | ||||
|             return *m_currentTracker; | ||||
|         } | ||||
|         void setCurrentTracker( ITracker* tracker ) { | ||||
|             m_currentTracker = tracker; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     class TrackerBase : public ITracker { | ||||
|     protected: | ||||
|         enum CycleState { | ||||
|             NotStarted, | ||||
|             Executing, | ||||
|             ExecutingChildren, | ||||
|             NeedsAnotherRun, | ||||
|             CompletedSuccessfully, | ||||
|             Failed | ||||
|         }; | ||||
|         class TrackerHasName { | ||||
|             std::string m_name; | ||||
|         public: | ||||
|             TrackerHasName( std::string const& name ) : m_name( name ) {} | ||||
|             bool operator ()( Ptr<ITracker> const& tracker ) { | ||||
|                 return tracker->name() == m_name; | ||||
|             } | ||||
|         }; | ||||
|         typedef std::vector<Ptr<ITracker> > Children; | ||||
|         std::string m_name; | ||||
|         TrackerContext& m_ctx; | ||||
|         ITracker* m_parent; | ||||
|         Children m_children; | ||||
|         CycleState m_runState; | ||||
|     public: | ||||
|         TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) | ||||
|         :   m_name( name ), | ||||
|             m_ctx( ctx ), | ||||
|             m_parent( parent ), | ||||
|             m_runState( NotStarted ) | ||||
|         {} | ||||
|         virtual ~TrackerBase(); | ||||
|  | ||||
|         virtual std::string name() const CATCH_OVERRIDE { | ||||
|             return m_name; | ||||
|         } | ||||
|         virtual bool isComplete() const CATCH_OVERRIDE { | ||||
|             return m_runState == CompletedSuccessfully || m_runState == Failed; | ||||
|         } | ||||
|         virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { | ||||
|             return m_runState == CompletedSuccessfully; | ||||
|         } | ||||
|         virtual bool isOpen() const CATCH_OVERRIDE { | ||||
|             return m_runState != NotStarted && !isComplete(); | ||||
|         } | ||||
|         virtual bool hasChildren() const CATCH_OVERRIDE { | ||||
|             return !m_children.empty(); | ||||
|         } | ||||
|          | ||||
|     private: | ||||
|         std::string m_name; | ||||
|         RunState m_runState; | ||||
|         TrackedSections m_children; | ||||
|         TrackedSection* m_parent;         | ||||
|     }; | ||||
|      | ||||
|     inline TrackedSection* TrackedSection::findChild( std::string const& childName ) { | ||||
|         TrackedSections::iterator it = m_children.find( childName ); | ||||
|         return it != m_children.end() | ||||
|             ? &it->second | ||||
|             : NULL; | ||||
|     } | ||||
|     inline TrackedSection* 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 ); | ||||
|     } | ||||
|     inline void TrackedSection::leave() { | ||||
|         for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); | ||||
|                 it != itEnd; | ||||
|                 ++it ) | ||||
|             if( it->second.runState() != Completed ) { | ||||
|  | ||||
|  | ||||
|         virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE { | ||||
|             m_children.push_back( child ); | ||||
|         } | ||||
|  | ||||
|         virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { | ||||
|             Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); | ||||
|             return( it != m_children.end() ) | ||||
|                 ? it->get() | ||||
|                 : CATCH_NULL; | ||||
|         } | ||||
|         virtual ITracker& parent() CATCH_OVERRIDE { | ||||
|             assert( m_parent ); // Should always be non-null except for root | ||||
|             return *m_parent; | ||||
|         } | ||||
|  | ||||
|         virtual void openChild() CATCH_OVERRIDE { | ||||
|             if( m_runState != ExecutingChildren ) { | ||||
|                 m_runState = ExecutingChildren; | ||||
|                 return; | ||||
|                 if( m_parent ) | ||||
|                     m_parent->openChild(); | ||||
|             } | ||||
|         m_runState = Completed; | ||||
|     } | ||||
|  | ||||
|     class TestCaseTracker { | ||||
|     public: | ||||
|         TestCaseTracker( std::string const& testCaseName ) | ||||
|         :   m_testCase( testCaseName, NULL ), | ||||
|             m_currentSection( &m_testCase ), | ||||
|             m_completedASectionThisRun( false ) | ||||
|         {} | ||||
|  | ||||
|         bool enterSection( std::string const& name ) { | ||||
|             TrackedSection* child = m_currentSection->acquireChild( name ); | ||||
|             if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed ) | ||||
|                 return false; | ||||
|  | ||||
|             m_currentSection = child; | ||||
|             m_currentSection->enter(); | ||||
|             return true; | ||||
|         } | ||||
|         void leaveSection() { | ||||
|             m_currentSection->leave(); | ||||
|             m_currentSection = m_currentSection->getParent(); | ||||
|             assert( m_currentSection != NULL ); | ||||
|             m_completedASectionThisRun = true; | ||||
|         void open() { | ||||
|             m_runState = Executing; | ||||
|             moveToThis(); | ||||
|             if( m_parent ) | ||||
|                 m_parent->openChild(); | ||||
|         } | ||||
|  | ||||
|         bool currentSectionHasChildren() const { | ||||
|             return m_currentSection->hasChildren(); | ||||
|         } | ||||
|         bool isCompleted() const { | ||||
|             return m_testCase.runState() == TrackedSection::Completed; | ||||
|         } | ||||
|         virtual void close() CATCH_OVERRIDE { | ||||
|  | ||||
|         class Guard { | ||||
|         public: | ||||
|             Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) { | ||||
|                 m_tracker.enterTestCase(); | ||||
|             // Close any still open children (e.g. generators) | ||||
|             while( &m_ctx.currentTracker() != this ) | ||||
|                 m_ctx.currentTracker().close(); | ||||
|  | ||||
|             switch( m_runState ) { | ||||
|                 case NotStarted: | ||||
|                 case CompletedSuccessfully: | ||||
|                 case Failed: | ||||
|                     throw std::logic_error( "Illogical state" ); | ||||
|  | ||||
|                 case NeedsAnotherRun: | ||||
|                     break;; | ||||
|  | ||||
|                 case Executing: | ||||
|                     m_runState = CompletedSuccessfully; | ||||
|                     break; | ||||
|                 case ExecutingChildren: | ||||
|                     if( m_children.empty() || m_children.back()->isComplete() ) | ||||
|                         m_runState = CompletedSuccessfully; | ||||
|                     break; | ||||
|  | ||||
|                 default: | ||||
|                     throw std::logic_error( "Unexpected state" ); | ||||
|             } | ||||
|             ~Guard() { | ||||
|                 m_tracker.leaveTestCase(); | ||||
|             } | ||||
|         private: | ||||
|             Guard( Guard const& ); | ||||
|             void operator = ( Guard const& ); | ||||
|             TestCaseTracker& m_tracker; | ||||
|         }; | ||||
|  | ||||
|             moveToParent(); | ||||
|             m_ctx.completeCycle(); | ||||
|         } | ||||
|         virtual void fail() CATCH_OVERRIDE { | ||||
|             m_runState = Failed; | ||||
|             if( m_parent ) | ||||
|                 m_parent->markAsNeedingAnotherRun(); | ||||
|             moveToParent(); | ||||
|             m_ctx.completeCycle(); | ||||
|         } | ||||
|         virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { | ||||
|             m_runState = NeedsAnotherRun; | ||||
|         } | ||||
|     private: | ||||
|         void enterTestCase() { | ||||
|             m_currentSection = &m_testCase; | ||||
|             m_completedASectionThisRun = false; | ||||
|             m_testCase.enter(); | ||||
|         void moveToParent() { | ||||
|             assert( m_parent ); | ||||
|             m_ctx.setCurrentTracker( m_parent ); | ||||
|         } | ||||
|         void leaveTestCase() { | ||||
|             m_testCase.leave(); | ||||
|         void moveToThis() { | ||||
|             m_ctx.setCurrentTracker( this ); | ||||
|         } | ||||
|  | ||||
|         TrackedSection m_testCase; | ||||
|         TrackedSection* m_currentSection; | ||||
|         bool m_completedASectionThisRun; | ||||
|     }; | ||||
|  | ||||
| } // namespace SectionTracking | ||||
|     class SectionTracker : public TrackerBase { | ||||
|     public: | ||||
|         SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) | ||||
|         :   TrackerBase( name, ctx, parent ) | ||||
|         {} | ||||
|         virtual ~SectionTracker(); | ||||
|  | ||||
| using SectionTracking::TestCaseTracker; | ||||
|         static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { | ||||
|             SectionTracker* section = CATCH_NULL; | ||||
|  | ||||
|             ITracker& currentTracker = ctx.currentTracker(); | ||||
|             if( ITracker* childTracker = currentTracker.findChild( name ) ) { | ||||
|                 section = dynamic_cast<SectionTracker*>( childTracker ); | ||||
|                 assert( section ); | ||||
|             } | ||||
|             else { | ||||
|                 section = new SectionTracker( name, ctx, ¤tTracker ); | ||||
|                 currentTracker.addChild( section ); | ||||
|             } | ||||
|             if( !ctx.completedCycle() && !section->isComplete() ) { | ||||
|  | ||||
|                 section->open(); | ||||
|             } | ||||
|             return *section; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     class IndexTracker : public TrackerBase { | ||||
|         int m_size; | ||||
|         int m_index; | ||||
|     public: | ||||
|         IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) | ||||
|         :   TrackerBase( name, ctx, parent ), | ||||
|             m_size( size ), | ||||
|             m_index( -1 ) | ||||
|         {} | ||||
|         virtual ~IndexTracker(); | ||||
|  | ||||
|         static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { | ||||
|             IndexTracker* tracker = CATCH_NULL; | ||||
|  | ||||
|             ITracker& currentTracker = ctx.currentTracker(); | ||||
|             if( ITracker* childTracker = currentTracker.findChild( name ) ) { | ||||
|                 tracker = dynamic_cast<IndexTracker*>( childTracker ); | ||||
|                 assert( tracker ); | ||||
|             } | ||||
|             else { | ||||
|                 tracker = new IndexTracker( name, ctx, ¤tTracker, size ); | ||||
|                 currentTracker.addChild( tracker ); | ||||
|             } | ||||
|  | ||||
|             if( !ctx.completedCycle() && !tracker->isComplete() ) { | ||||
|                 if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) | ||||
|                     tracker->moveNext(); | ||||
|                 tracker->open(); | ||||
|             } | ||||
|  | ||||
|             return *tracker; | ||||
|         } | ||||
|  | ||||
|         int index() const { return m_index; } | ||||
|  | ||||
|         void moveNext() { | ||||
|             m_index++; | ||||
|             m_children.clear(); | ||||
|         } | ||||
|  | ||||
|         virtual void close() CATCH_OVERRIDE { | ||||
|             TrackerBase::close(); | ||||
|             if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) | ||||
|                 m_runState = Executing; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     inline ITracker& TrackerContext::startRun() { | ||||
|         m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); | ||||
|         m_currentTracker = CATCH_NULL; | ||||
|         m_runState = Executing; | ||||
|         return *m_rootTracker; | ||||
|     } | ||||
|  | ||||
| } // namespace TestCaseTracking | ||||
|  | ||||
| using TestCaseTracking::ITracker; | ||||
| using TestCaseTracking::TrackerContext; | ||||
| using TestCaseTracking::SectionTracker; | ||||
| using TestCaseTracking::IndexTracker; | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
|   | ||||
| @@ -42,27 +42,32 @@ struct NameAndDesc { | ||||
|     const char* description; | ||||
| }; | ||||
|  | ||||
| void registerTestCase | ||||
|     (   ITestCase* testCase, | ||||
|         char const* className, | ||||
|         NameAndDesc const& nameAndDesc, | ||||
|         SourceLineInfo const& lineInfo ); | ||||
|  | ||||
| struct AutoReg { | ||||
|  | ||||
|     AutoReg(    TestFunction function, | ||||
|                 SourceLineInfo const& lineInfo, | ||||
|                 NameAndDesc const& nameAndDesc ); | ||||
|     AutoReg | ||||
|         (   TestFunction function, | ||||
|             SourceLineInfo const& lineInfo, | ||||
|             NameAndDesc const& nameAndDesc ); | ||||
|  | ||||
|     template<typename C> | ||||
|     AutoReg(    void (C::*method)(), | ||||
|                 char const* className, | ||||
|                 NameAndDesc const& nameAndDesc, | ||||
|                 SourceLineInfo const& lineInfo ) { | ||||
|         registerTestCase(   new MethodTestCase<C>( method ), | ||||
|                             className, | ||||
|                             nameAndDesc, | ||||
|                             lineInfo ); | ||||
|     } | ||||
|     AutoReg | ||||
|         (   void (C::*method)(), | ||||
|             char const* className, | ||||
|             NameAndDesc const& nameAndDesc, | ||||
|             SourceLineInfo const& lineInfo ) { | ||||
|  | ||||
|     void registerTestCase(  ITestCase* testCase, | ||||
|                             char const* className, | ||||
|                             NameAndDesc const& nameAndDesc, | ||||
|                             SourceLineInfo const& lineInfo ); | ||||
|         registerTestCase | ||||
|             (   new MethodTestCase<C>( method ), | ||||
|                 className, | ||||
|                 nameAndDesc, | ||||
|                 lineInfo ); | ||||
|     } | ||||
|  | ||||
|     ~AutoReg(); | ||||
|  | ||||
| @@ -71,6 +76,11 @@ private: | ||||
|     void operator= ( AutoReg const& ); | ||||
| }; | ||||
|  | ||||
| void registerTestCaseFunction | ||||
|     (   TestFunction function, | ||||
|         SourceLineInfo const& lineInfo, | ||||
|         NameAndDesc const& nameAndDesc ); | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| #ifdef CATCH_CONFIG_VARIADIC_MACROS | ||||
| @@ -94,6 +104,10 @@ private: | ||||
|         } \ | ||||
|         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////////// | ||||
|     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ | ||||
|         Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); | ||||
|  | ||||
| #else | ||||
|     /////////////////////////////////////////////////////////////////////////////// | ||||
|     #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ | ||||
| @@ -115,6 +129,9 @@ private: | ||||
|         } \ | ||||
|         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////////// | ||||
|     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ | ||||
|         Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); | ||||
| #endif | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED | ||||
|   | ||||
| @@ -13,6 +13,7 @@ | ||||
| #pragma clang diagnostic ignored "-Wpadded" | ||||
| #endif | ||||
|  | ||||
| #include "catch_wildcard_pattern.hpp" | ||||
| #include "catch_test_case_info.h" | ||||
|  | ||||
| #include <string> | ||||
| @@ -26,50 +27,18 @@ namespace Catch { | ||||
|             virtual bool matches( TestCaseInfo const& testCase ) const = 0; | ||||
|         }; | ||||
|         class NamePattern : public Pattern { | ||||
|             enum WildcardPosition { | ||||
|                 NoWildcard = 0, | ||||
|                 WildcardAtStart = 1, | ||||
|                 WildcardAtEnd = 2, | ||||
|                 WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd | ||||
|             }; | ||||
|  | ||||
|         public: | ||||
|             NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) { | ||||
|                 if( startsWith( m_name, "*" ) ) { | ||||
|                     m_name = m_name.substr( 1 ); | ||||
|                     m_wildcard = WildcardAtStart; | ||||
|                 } | ||||
|                 if( endsWith( m_name, "*" ) ) { | ||||
|                     m_name = m_name.substr( 0, m_name.size()-1 ); | ||||
|                     m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); | ||||
|                 } | ||||
|             } | ||||
|             NamePattern( std::string const& name ) | ||||
|             : m_wildcardPattern( toLower( name ), CaseSensitive::No ) | ||||
|             {} | ||||
|             virtual ~NamePattern(); | ||||
|             virtual bool matches( TestCaseInfo const& testCase ) const { | ||||
|                 switch( m_wildcard ) { | ||||
|                     case NoWildcard: | ||||
|                         return m_name == toLower( testCase.name ); | ||||
|                     case WildcardAtStart: | ||||
|                         return endsWith( toLower( testCase.name ), m_name ); | ||||
|                     case WildcardAtEnd: | ||||
|                         return startsWith( toLower( testCase.name ), m_name ); | ||||
|                     case WildcardAtBothEnds: | ||||
|                         return contains( toLower( testCase.name ), m_name ); | ||||
|                 } | ||||
|  | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic push | ||||
| #pragma clang diagnostic ignored "-Wunreachable-code" | ||||
| #endif | ||||
|                 throw std::logic_error( "Unknown enum" ); | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic pop | ||||
| #endif | ||||
|                 return m_wildcardPattern.matches( toLower( testCase.name ) ); | ||||
|             } | ||||
|         private: | ||||
|             std::string m_name; | ||||
|             WildcardPosition m_wildcard; | ||||
|             WildcardPattern m_wildcardPattern; | ||||
|         }; | ||||
|  | ||||
|         class TagPattern : public Pattern { | ||||
|         public: | ||||
|             TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} | ||||
| @@ -80,6 +49,7 @@ namespace Catch { | ||||
|         private: | ||||
|             std::string m_tag; | ||||
|         }; | ||||
|  | ||||
|         class ExcludedPattern : public Pattern { | ||||
|         public: | ||||
|             ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} | ||||
|   | ||||
| @@ -37,7 +37,7 @@ namespace Catch { | ||||
| #else | ||||
|         uint64_t getCurrentTicks() { | ||||
|             timeval t; | ||||
|             gettimeofday(&t,NULL); | ||||
|             gettimeofday(&t,CATCH_NULL); | ||||
|             return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec ); | ||||
|         } | ||||
| #endif | ||||
|   | ||||
| @@ -52,6 +52,11 @@ std::string toString( char value ); | ||||
| std::string toString( signed char value ); | ||||
| std::string toString( unsigned char value ); | ||||
|  | ||||
| #ifdef CATCH_CONFIG_CPP11_LONG_LONG | ||||
| std::string toString( long long value ); | ||||
| std::string toString( unsigned long long value ); | ||||
| #endif | ||||
|  | ||||
| #ifdef CATCH_CONFIG_CPP11_NULLPTR | ||||
| std::string toString( std::nullptr_t ); | ||||
| #endif | ||||
| @@ -62,10 +67,10 @@ std::string toString( std::nullptr_t ); | ||||
|     std::string toString( NSObject* const& nsObject ); | ||||
| #endif | ||||
|  | ||||
|    | ||||
|  | ||||
| namespace Detail { | ||||
|  | ||||
|     extern std::string unprintableString; | ||||
|     extern const std::string unprintableString; | ||||
|  | ||||
|     struct BorgType { | ||||
|         template<typename T> BorgType( T const& ); | ||||
| @@ -73,7 +78,7 @@ namespace Detail { | ||||
|  | ||||
|     struct TrueType { char sizer[1]; }; | ||||
|     struct FalseType { char sizer[2]; }; | ||||
|      | ||||
|  | ||||
|     TrueType& testStreamable( std::ostream& ); | ||||
|     FalseType testStreamable( FalseType ); | ||||
|  | ||||
| @@ -148,7 +153,7 @@ struct StringMaker<T*> { | ||||
|     template<typename U> | ||||
|     static std::string convert( U* p ) { | ||||
|         if( !p ) | ||||
|             return INTERNAL_CATCH_STRINGIFY( NULL ); | ||||
|             return "NULL"; | ||||
|         else | ||||
|             return Detail::rawMemoryToString( p ); | ||||
|     } | ||||
| @@ -158,7 +163,7 @@ template<typename R, typename C> | ||||
| struct StringMaker<R C::*> { | ||||
|     static std::string convert( R C::* p ) { | ||||
|         if( !p ) | ||||
|             return INTERNAL_CATCH_STRINGIFY( NULL ); | ||||
|             return "NULL"; | ||||
|         else | ||||
|             return Detail::rawMemoryToString( p ); | ||||
|     } | ||||
|   | ||||
| @@ -15,9 +15,11 @@ namespace Catch { | ||||
|  | ||||
| namespace Detail { | ||||
|  | ||||
|     std::string unprintableString = "{?}"; | ||||
|     const std::string unprintableString = "{?}"; | ||||
|  | ||||
|     namespace { | ||||
|         const int hexThreshold = 255; | ||||
|  | ||||
|         struct Endianness { | ||||
|             enum Arch { Big, Little }; | ||||
|  | ||||
| @@ -99,7 +101,7 @@ std::string toString( wchar_t* const value ) | ||||
| std::string toString( int value ) { | ||||
|     std::ostringstream oss; | ||||
|     oss << value; | ||||
|     if( value >= 255 ) | ||||
|     if( value > Detail::hexThreshold ) | ||||
|         oss << " (0x" << std::hex << value << ")"; | ||||
|     return oss.str(); | ||||
| } | ||||
| @@ -107,7 +109,7 @@ std::string toString( int value ) { | ||||
| std::string toString( unsigned long value ) { | ||||
|     std::ostringstream oss; | ||||
|     oss << value; | ||||
|     if( value >= 255 ) | ||||
|     if( value > Detail::hexThreshold ) | ||||
|         oss << " (0x" << std::hex << value << ")"; | ||||
|     return oss.str(); | ||||
| } | ||||
| @@ -157,6 +159,23 @@ std::string toString( unsigned char value ) { | ||||
|     return toString( static_cast<char>( value ) ); | ||||
| } | ||||
|  | ||||
| #ifdef CATCH_CONFIG_CPP11_LONG_LONG | ||||
| std::string toString( long long value ) { | ||||
|     std::ostringstream oss; | ||||
|     oss << value; | ||||
|     if( value > Detail::hexThreshold ) | ||||
|         oss << " (0x" << std::hex << value << ")"; | ||||
|     return oss.str(); | ||||
| } | ||||
| std::string toString( unsigned long long value ) { | ||||
|     std::ostringstream oss; | ||||
|     oss << value; | ||||
|     if( value > Detail::hexThreshold ) | ||||
|         oss << " (0x" << std::hex << value << ")"; | ||||
|     return oss.str(); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef CATCH_CONFIG_CPP11_NULLPTR | ||||
| std::string toString( std::nullptr_t ) { | ||||
|     return "nullptr"; | ||||
|   | ||||
| @@ -27,7 +27,7 @@ namespace Catch { | ||||
|         unsigned int const buildNumber; | ||||
|  | ||||
|         friend std::ostream& operator << ( std::ostream& os, Version const& version ); | ||||
|          | ||||
|  | ||||
|     private: | ||||
|         void operator=( Version const& ); | ||||
|     }; | ||||
|   | ||||
| @@ -37,7 +37,7 @@ namespace Catch { | ||||
|         return os; | ||||
|     } | ||||
|  | ||||
|     Version libraryVersion( 1, 2, 1, "", 0 ); | ||||
|     Version libraryVersion( 1, 3, 0, "", 0 ); | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										71
									
								
								include/internal/catch_wildcard_pattern.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								include/internal/catch_wildcard_pattern.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
|  *  Created by Phil on 13/7/2015. | ||||
|  *  Copyright 2015 Two Blue Cubes Ltd. All rights reserved. | ||||
|  * | ||||
|  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
|  */ | ||||
| #ifndef TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED | ||||
|  | ||||
| #include "catch_common.h" | ||||
|  | ||||
| namespace Catch | ||||
| { | ||||
|     class WildcardPattern { | ||||
|         enum WildcardPosition { | ||||
|             NoWildcard = 0, | ||||
|             WildcardAtStart = 1, | ||||
|             WildcardAtEnd = 2, | ||||
|             WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd | ||||
|         }; | ||||
|  | ||||
|     public: | ||||
|  | ||||
|         WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) | ||||
|         :   m_caseSensitivity( caseSensitivity ), | ||||
|             m_wildcard( NoWildcard ), | ||||
|             m_pattern( adjustCase( pattern ) ) | ||||
|         { | ||||
|             if( startsWith( m_pattern, "*" ) ) { | ||||
|                 m_pattern = m_pattern.substr( 1 ); | ||||
|                 m_wildcard = WildcardAtStart; | ||||
|             } | ||||
|             if( endsWith( m_pattern, "*" ) ) { | ||||
|                 m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); | ||||
|                 m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); | ||||
|             } | ||||
|         } | ||||
|         virtual ~WildcardPattern(); | ||||
|         virtual bool matches( std::string const& str ) const { | ||||
|             switch( m_wildcard ) { | ||||
|                 case NoWildcard: | ||||
|                     return m_pattern == adjustCase( str ); | ||||
|                 case WildcardAtStart: | ||||
|                     return endsWith( adjustCase( str ), m_pattern ); | ||||
|                 case WildcardAtEnd: | ||||
|                     return startsWith( adjustCase( str ), m_pattern ); | ||||
|                 case WildcardAtBothEnds: | ||||
|                     return contains( adjustCase( str ), m_pattern ); | ||||
|             } | ||||
|  | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic push | ||||
| #pragma clang diagnostic ignored "-Wunreachable-code" | ||||
| #endif | ||||
|             throw std::logic_error( "Unknown enum" ); | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic pop | ||||
| #endif | ||||
|         } | ||||
|     private: | ||||
|         std::string adjustCase( std::string const& str ) const { | ||||
|             return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; | ||||
|         } | ||||
|         CaseSensitive::Choice m_caseSensitivity; | ||||
|         WildcardPosition m_wildcard; | ||||
|         std::string m_pattern; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED | ||||
| @@ -8,14 +8,72 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED | ||||
|  | ||||
| #include "../internal/catch_stream.h" | ||||
| #include "catch_stream.h" | ||||
| #include "catch_compiler_capabilities.h" | ||||
| #include "catch_suppress_warnings.h" | ||||
|  | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <iomanip> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     class XmlEncode { | ||||
|     public: | ||||
|         enum ForWhat { ForTextNodes, ForAttributes }; | ||||
|  | ||||
|         XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) | ||||
|         :   m_str( str ), | ||||
|             m_forWhat( forWhat ) | ||||
|         {} | ||||
|  | ||||
|         void encodeTo( std::ostream& os ) const { | ||||
|  | ||||
|             // Apostrophe escaping not necessary if we always use " to write attributes | ||||
|             // (see: http://www.w3.org/TR/xml/#syntax) | ||||
|  | ||||
|             for( std::size_t i = 0; i < m_str.size(); ++ i ) { | ||||
|                 char c = m_str[i]; | ||||
|                 switch( c ) { | ||||
|                     case '<':   os << "<"; break; | ||||
|                     case '&':   os << "&"; break; | ||||
|  | ||||
|                     case '>': | ||||
|                         // See: http://www.w3.org/TR/xml/#syntax | ||||
|                         if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) | ||||
|                             os << ">"; | ||||
|                         else | ||||
|                             os << c; | ||||
|                         break; | ||||
|  | ||||
|                     case '\"': | ||||
|                         if( m_forWhat == ForAttributes ) | ||||
|                             os << """; | ||||
|                         else | ||||
|                             os << c; | ||||
|                         break; | ||||
|  | ||||
|                     default: | ||||
|                         // Escape control chars - based on contribution by @espenalb in PR #465 | ||||
|                         if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) | ||||
|                             os << "&#x" << std::uppercase << std::hex << static_cast<int>( c ); | ||||
|                         else | ||||
|                             os << c; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { | ||||
|             xmlEncode.encodeTo( os ); | ||||
|             return os; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         std::string m_str; | ||||
|         ForWhat m_forWhat; | ||||
|     }; | ||||
|  | ||||
|     class XmlWriter { | ||||
|     public: | ||||
|  | ||||
| @@ -27,7 +85,7 @@ namespace Catch { | ||||
|  | ||||
|             ScopedElement( ScopedElement const& other ) | ||||
|             :   m_writer( other.m_writer ){ | ||||
|                 other.m_writer = NULL; | ||||
|                 other.m_writer = CATCH_NULL; | ||||
|             } | ||||
|  | ||||
|             ~ScopedElement() { | ||||
| @@ -98,11 +156,8 @@ namespace Catch { | ||||
|         } | ||||
|  | ||||
|         XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { | ||||
|             if( !name.empty() && !attribute.empty() ) { | ||||
|                 stream() << " " << name << "=\""; | ||||
|                 writeEncodedText( attribute ); | ||||
|                 stream() << "\""; | ||||
|             } | ||||
|             if( !name.empty() && !attribute.empty() ) | ||||
|                 stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; | ||||
|             return *this; | ||||
|         } | ||||
|  | ||||
| @@ -113,9 +168,9 @@ namespace Catch { | ||||
|  | ||||
|         template<typename T> | ||||
|         XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { | ||||
|             if( !name.empty() ) | ||||
|                 stream() << " " << name << "=\"" << attribute << "\""; | ||||
|             return *this; | ||||
|             std::ostringstream oss; | ||||
|             oss << attribute; | ||||
|             return writeAttribute( name, oss.str() ); | ||||
|         } | ||||
|  | ||||
|         XmlWriter& writeText( std::string const& text, bool indent = true ) { | ||||
| @@ -124,7 +179,7 @@ namespace Catch { | ||||
|                 ensureTagClosed(); | ||||
|                 if( tagWasOpen && indent ) | ||||
|                     stream() << m_indent; | ||||
|                 writeEncodedText( text ); | ||||
|                 stream() << XmlEncode( text ); | ||||
|                 m_needsNewline = true; | ||||
|             } | ||||
|             return *this; | ||||
| @@ -169,30 +224,6 @@ namespace Catch { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         void writeEncodedText( std::string const& text ) { | ||||
|             static const char* charsToEncode = "<&\""; | ||||
|             std::string mtext = text; | ||||
|             std::string::size_type pos = mtext.find_first_of( charsToEncode ); | ||||
|             while( pos != std::string::npos ) { | ||||
|                 stream() << mtext.substr( 0, pos ); | ||||
|  | ||||
|                 switch( mtext[pos] ) { | ||||
|                     case '<': | ||||
|                         stream() << "<"; | ||||
|                         break; | ||||
|                     case '&': | ||||
|                         stream() << "&"; | ||||
|                         break; | ||||
|                     case '\"': | ||||
|                         stream() << """; | ||||
|                         break; | ||||
|                 } | ||||
|                 mtext = mtext.substr( pos+1 ); | ||||
|                 pos = mtext.find_first_of( charsToEncode ); | ||||
|             } | ||||
|             stream() << mtext; | ||||
|         } | ||||
|  | ||||
|         bool m_tagIsOpen; | ||||
|         bool m_needsNewline; | ||||
|         std::vector<std::string> m_tags; | ||||
| @@ -201,4 +232,6 @@ namespace Catch { | ||||
|     }; | ||||
|  | ||||
| } | ||||
| #include "catch_reenable_warnings.h" | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Phil Nash
					Phil Nash