diff --git a/include/catch.hpp b/include/catch.hpp index 778f2c8e..9da979f6 100644 --- a/include/catch.hpp +++ b/include/catch.hpp @@ -19,6 +19,7 @@ #if (_MANAGED == 1) || (_M_CEE == 1) // detect CLR #define INTERNAL_CATCH_VS_MANAGED + #define INTERNAL_CATCH_INLINE inline #else #if defined(_WINDLL) @@ -26,6 +27,7 @@ // It's possible that this is not enough for someone so allow it to be overridden... #if !defined( CATCH_CONFIG_MAIN ) && !defined( CATCH_CONFIG_RUNNER ) #define INTERNAL_CATCH_VS_NATIVE + #define INTERNAL_CATCH_INLINE inline #endif #endif @@ -52,7 +54,6 @@ #endif #if defined(INTERNAL_CATCH_VS_MANAGED) || defined(INTERNAL_CATCH_VS_NATIVE) - #define INTERNAL_CATCH_INLINE inline #ifdef INTERNAL_CATCH_VS_MANAGED #include "internal/catch_vs_managed_impl.hpp" #else // INTERNAL_CATCH_VS_MANAGED @@ -203,6 +204,16 @@ #define THEN( desc ) SECTION( " Then: " desc, "" ) #define AND_THEN( desc ) SECTION( " And: " desc, "" ) +#if defined(INTERNAL_CATCH_VS_MANAGED) || defined(INTERNAL_CATCH_VS_NATIVE) +#define CATCH_MAP_CATEGORY_TO_TAG( Category, Tag ) INTERNAL_CATCH_MAP_CATEGORY_TO_TAG( Category, Tag ) +#define CATCH_CONFIG_SHOW_SUCCESS( v ) CATCH_INTERNAL_CONFIG_SHOW_SUCCESS( v ) +#define CATCH_CONFIG_WARN_MISSING_ASSERTIONS( v ) CATCH_INTERNAL_CONFIG_WARN_MISSING_ASSERTIONS( v ) +#else +#define CATCH_MAP_CATEGORY_TO_TAG( Category, Tag ) +#define CATCH_CONFIG_SHOW_SUCCESS( v ) +#define CATCH_CONFIG_WARN_MISSING_ASSERTIONS( v ) +#endif + using Catch::Detail::Approx; #ifdef __clang__ diff --git a/include/catch_runner.hpp b/include/catch_runner.hpp index d72ab5aa..52a475e8 100644 --- a/include/catch_runner.hpp +++ b/include/catch_runner.hpp @@ -108,6 +108,8 @@ namespace Catch { std::set m_testsAlreadyRun; }; +#if !defined(INTERNAL_CATCH_VS_MANAGED) && !defined(INTERNAL_CATCH_VS_NATIVE) + class Session { static bool alreadyInstantiated; @@ -232,6 +234,8 @@ namespace Catch { bool Session::alreadyInstantiated = false; +#endif // !VS_MANAGED && !VS_NATIVE + } // end namespace Catch #endif // TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index eaef9237..7f4f0468 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -56,11 +56,7 @@ namespace Catch { .setResultType( matcher.match( arg ) ); } -#if defined(INTERNAL_CATCH_VS_MANAGED) - // TestFailureException not defined for CLR -#else // detect CLR -struct TestFailureException{}; -#endif + struct TestFailureException{}; } // end namespace Catch @@ -70,7 +66,6 @@ struct TestFailureException{}; #if !defined(INTERNAL_CATCH_VS_MANAGED) && !defined(INTERNAL_CATCH_VS_NATIVE) // normal Catch - #define INTERNAL_CATCH_TEST_FAILURE_EXCEPTION const Catch::TestFailureException& #define INTERNAL_CATCH_TEST_THROW_FAILURE throw Catch::TestFailureException(); #else // VS integration @@ -84,10 +79,9 @@ struct TestFailureException{}; std::stringstream _sf; \ _sf << r->getExpressionInMacro().c_str() << ", " << r->getMessage().c_str(); \ std::string fail = _sf.str(); \ - Assert::Fail(Catch::convert_string_to_managed(fail)); \ + Assert::Fail(Catch::convert_string_for_assert(fail)); \ } - #define INTERNAL_CATCH_TEST_FAILURE_EXCEPTION AssertFailedException^ #else #if defined(INTERNAL_CATCH_VS_NATIVE) @@ -106,8 +100,6 @@ struct TestFailureException{}; Assert::Fail(ws2.c_str(), &li); \ } - #define INTERNAL_CATCH_TEST_FAILURE_EXCEPTION const Catch::TestFailureException& - #endif // INTERNAL_CATCH_VS_MANAGED #endif // detect CLR @@ -119,7 +111,7 @@ struct TestFailureException{}; if( Catch::ResultAction::Value internal_catch_action = Catch::getResultCapture().acceptExpression( evaluatedExpr, INTERNAL_CATCH_ASSERTIONINFO_NAME ) ) { \ if( internal_catch_action & Catch::ResultAction::Debug ) BreakIntoDebugger(); \ if( internal_catch_action & Catch::ResultAction::Abort ) { INTERNAL_CATCH_TEST_THROW_FAILURE } \ - if( !Catch::shouldContinueOnFailure( resultDisposition ) ) { INTERNAL_CATCH_TEST_THROW_FAILURE } \ + if( !Catch::shouldContinueOnFailure( resultDisposition ) ) { throw Catch::TestFailureException(); } \ Catch::isTrue( false && originalExpr ); \ } @@ -133,7 +125,7 @@ struct TestFailureException{}; INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ try { \ INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionDecomposer()->*expr ).endExpression( resultDisposition ), resultDisposition, expr ); \ - } catch( INTERNAL_CATCH_TEST_FAILURE_EXCEPTION ) { \ + } catch( const Catch::TestFailureException& ) { \ throw; \ } catch( ... ) { \ INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException(), \ @@ -172,7 +164,7 @@ struct TestFailureException{}; INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::DidntThrowException ), resultDisposition, false ); \ } \ } \ - catch( INTERNAL_CATCH_TEST_FAILURE_EXCEPTION ) { \ + catch( const Catch::TestFailureException& ) { \ throw; \ } \ catch( exceptionType ) { \ @@ -216,7 +208,7 @@ struct TestFailureException{}; INTERNAL_CATCH_ACCEPT_INFO( #arg " " #matcher, macroName, resultDisposition ); \ try { \ INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::expressionResultBuilderFromMatcher( ::Catch::Matchers::matcher, arg, #matcher ) ), resultDisposition, false ); \ - } catch( INTERNAL_CATCH_TEST_FAILURE_EXCEPTION ) { \ + } catch( const Catch::TestFailureException& ) { \ throw; \ } catch( ... ) { \ INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException() ), \ diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index f561b260..301d3979 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -22,6 +22,138 @@ #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif +namespace CatchOverrides { + + class ConfigGuard + { + public: + ConfigGuard() + : origConfig(Catch::getCurrentContext().getConfig()) + {} + ~ConfigGuard() + { + Catch::getCurrentMutableContext().setConfig(origConfig); + } + const Catch::Ptr& value() const {return origConfig;} + private: + ConfigGuard(const ConfigGuard&); + ConfigGuard& operator=(const ConfigGuard&); + + const Catch::Ptr origConfig; + }; + + template + class Config + { + typedef std::map LineData; + typedef std::map FileLineData; + public: + bool includeSuccessfulResults(const std::string& file, int c) const + { + bool result(false); + FileLineData::const_iterator it = showSuccessfulTestsData.find(file); + if( it != showSuccessfulTestsData.end() ) + { + for( LineData::const_iterator lineIt = it->second.begin(); lineIt != it->second.end(); ++lineIt ) + { + if( c <= lineIt->first ) + break; + result = lineIt->second; + } + } + return result; + } + void insertSuccessfulResults(const std::string& file, int c, bool v) + { + FileLineData::iterator it = showSuccessfulTestsData.find(file); + if( it == showSuccessfulTestsData.end() ) + { + LineData tmp; + tmp.insert(std::make_pair(c,v)); + showSuccessfulTestsData.insert(std::make_pair(file, tmp)); + } + else + { + it->second.insert(std::make_pair(c,v)); + } + } + bool warnAboutMissingAssertions(const std::string& file, int c) const + { + bool result(false); + FileLineData::const_iterator it = missingAssertionData.find(file); + if( it != missingAssertionData.end() ) + { + for( LineData::const_iterator lineIt = it->second.begin(); lineIt != it->second.end(); ++lineIt ) + { + if( c <= lineIt->first ) + break; + result = lineIt->second; + } + } + return result; + } + void insertMissingAssertions(const std::string& file, int c, bool v) + { + FileLineData::iterator it = missingAssertionData.find(file); + if( it == missingAssertionData.end() ) + { + LineData tmp; + tmp.insert(std::make_pair(c,v)); + missingAssertionData.insert(std::make_pair(file, tmp)); + } + else + { + it->second.insert(std::make_pair(c,v)); + } + } + static Config& instance() + { + if( !s_instance ) + { + s_instance = new Config(); + } + return *s_instance; + } + private: + FileLineData showSuccessfulTestsData; + FileLineData missingAssertionData; + + static Config* s_instance; + }; + template + Config* Config::s_instance = NULL; + + template + struct ConfigReset + { + ConfigReset( const std::string& file, int c ) + { + Config::instance().insertSuccessfulResults(file, c, false); + Config::instance().insertMissingAssertions(file, c, false); + } + }; + + template + struct ConfigShowSuccessfulTests + { + template + ConfigShowSuccessfulTests( const std::string& file, int c, U v ) + { + Config::instance().insertSuccessfulResults(file, c, v ? true : false); + } + }; + + template + struct ConfigWarnMissingAssertions + { + template + ConfigWarnMissingAssertions( const std::string& file, int c, U v ) + { + Config::instance().insertMissingAssertions(file, c, v ? true : false); + } + }; +} + namespace Catch { struct ConfigData { @@ -40,6 +172,21 @@ namespace Catch { showDurations( ShowDurations::DefaultForReporter ) {} + explicit ConfigData(const IConfig* other) + : listTests( false ), + listTags( false ), + listReporters( false ), + showSuccessfulTests( other ? other->includeSuccessfulResults() : false ), + shouldDebugBreak( false ), + noThrow( other ? !other->allowThrows() : false ), + showHelp( false ), + abortAfter( -1 ), + verbosity( Verbosity::Normal ), + warnings( other ? (other->warnAboutMissingAssertions() ? WarnAbout::NoAssertions : WarnAbout::Nothing) : WarnAbout::Nothing ), + showDurations( other ? other->showDurations() : ShowDurations::DefaultForReporter ), + name( other ? other->name() : std::string() ) + {} + bool listTests; bool listTags; bool listReporters; diff --git a/include/internal/catch_console_colour.hpp b/include/internal/catch_console_colour.hpp index 3fc2e38b..40a6c999 100644 --- a/include/internal/catch_console_colour.hpp +++ b/include/internal/catch_console_colour.hpp @@ -12,10 +12,6 @@ namespace Catch { - namespace Detail { - struct IColourImpl; - } - struct Colour { enum Code { None = 0, @@ -53,12 +49,6 @@ namespace Catch { // Use constructed object for RAII guard Colour( Code _colourCode ); ~Colour(); - - // Use static method for one-shot changes - static void use( Code _colourCode ); - - private: - static Detail::IColourImpl* impl; }; } // end namespace Catch diff --git a/include/internal/catch_console_colour_impl.hpp b/include/internal/catch_console_colour_impl.hpp index 0183f785..bfe37b03 100644 --- a/include/internal/catch_console_colour_impl.hpp +++ b/include/internal/catch_console_colour_impl.hpp @@ -10,13 +10,6 @@ #include "catch_console_colour.hpp" -namespace Catch { namespace Detail { - struct IColourImpl { - virtual ~IColourImpl() {} - virtual void use( Colour::Code _colourCode ) = 0; - }; -}} - #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// #ifndef NOMINMAX @@ -32,7 +25,7 @@ namespace Catch { namespace Detail { namespace Catch { namespace { - class Win32ColourImpl : public Detail::IColourImpl { + class Win32ColourImpl { public: Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) { @@ -41,7 +34,7 @@ namespace { originalAttributes = csbiInfo.wAttributes; } - virtual void use( Colour::Code _colourCode ) { + void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); @@ -73,7 +66,7 @@ namespace { return true; } - Win32ColourImpl platformColourImpl; + typedef Win32ColourImpl PlatformColourImpl; } // end anon namespace } // end namespace Catch @@ -89,9 +82,9 @@ namespace { // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 - class PosixColourImpl : public Detail::IColourImpl { + class PosixColourImpl { public: - virtual void use( Colour::Code _colourCode ) { + void use( Colour::Code _colourCode ) { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); @@ -120,7 +113,7 @@ namespace { return isatty( fileno(stdout) ); } - PosixColourImpl platformColourImpl; + typedef PosixColourImpl PlatformColourImpl; } // end anon namespace } // end namespace Catch @@ -129,24 +122,24 @@ namespace { namespace Catch { - namespace { - struct NoColourImpl : Detail::IColourImpl { - void use( Colour::Code ) {} - }; - NoColourImpl noColourImpl; - static const bool shouldUseColour = shouldUseColourForPlatform() && - !isDebuggerActive(); - } + template + struct ColourChange + { + static Impl impl; + static const bool shouldUseColour; + }; + template + Impl ColourChange::impl; + template + const bool ColourChange::shouldUseColour = shouldUseColourForPlatform() && + !isDebuggerActive();; - Colour::Colour( Code _colourCode ){ use( _colourCode ); } - Colour::~Colour(){ use( None ); } - void Colour::use( Code _colourCode ) { - impl->use( _colourCode ); + INTERNAL_CATCH_INLINE Colour::Colour( Code _colourCode ) { + if( ColourChange::shouldUseColour ) ColourChange::impl.use( _colourCode ); + } + INTERNAL_CATCH_INLINE Colour::~Colour() { + if( ColourChange::shouldUseColour ) ColourChange::impl.use( Colour::None ); } - - Detail::IColourImpl* Colour::impl = shouldUseColour - ? static_cast( &platformColourImpl ) - : static_cast( &noColourImpl ); } // end namespace Catch diff --git a/include/internal/catch_context_impl.hpp b/include/internal/catch_context_impl.hpp index d70ea358..7d217e5c 100644 --- a/include/internal/catch_context_impl.hpp +++ b/include/internal/catch_context_impl.hpp @@ -15,9 +15,10 @@ namespace Catch { + template class Context : public IMutableContext { - Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} + Context() : m_config( NULL ), m_runner( &nullRunner ), m_resultCapture( &nullResultCapture ) {} Context( Context const& ); void operator=( Context const& ); @@ -81,15 +82,28 @@ namespace Catch { IRunner* m_runner; IResultCapture* m_resultCapture; std::map m_generatorsByTestName; + + static ResultCapture nullResultCapture; + static Runner nullRunner; + public: + static Context* currentContext; }; - namespace { + template + ResultCapture Context::nullResultCapture; + template + Runner Context::nullRunner; + template + Context* Context::currentContext = NULL; + + /*namespace { Context* currentContext = NULL; - } + }*/ + typedef Context DefaultContext; INTERNAL_CATCH_INLINE IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; + if( !DefaultContext::currentContext ) + DefaultContext::currentContext = new DefaultContext(); + return *DefaultContext::currentContext; } INTERNAL_CATCH_INLINE IContext& getCurrentContext() { return getCurrentMutableContext(); @@ -104,8 +118,8 @@ namespace Catch { } INTERNAL_CATCH_INLINE void cleanUpContext() { - delete currentContext; - currentContext = NULL; + delete DefaultContext::currentContext; + DefaultContext::currentContext = NULL; } } diff --git a/include/internal/catch_interfaces_capture.h b/include/internal/catch_interfaces_capture.h index 2601ab46..db9958ec 100644 --- a/include/internal/catch_interfaces_capture.h +++ b/include/internal/catch_interfaces_capture.h @@ -41,6 +41,23 @@ namespace Catch { virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; }; + + struct NullResultCapture : public IResultCapture { + + virtual void assertionEnded( AssertionResult const& ) {} + virtual bool sectionStarted( SectionInfo const& , + Counts& ) {return false;} + virtual void sectionEnded( SectionInfo const& , Counts const& , double ) {} + virtual void pushScopedMessage( MessageInfo const& ) {} + virtual void popScopedMessage( MessageInfo const& ) {} + + virtual bool shouldDebugBreak() const {return false;} + + virtual ResultAction::Value acceptExpression( ExpressionResultBuilder const& , AssertionInfo const& ) {return ResultAction::Abort;} + + virtual std::string getCurrentTestName() const {return std::string();} + virtual const AssertionResult* getLastResult() const {return NULL;} + }; } #endif // TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED diff --git a/include/internal/catch_interfaces_runner.h b/include/internal/catch_interfaces_runner.h index ab127b9f..48c836aa 100644 --- a/include/internal/catch_interfaces_runner.h +++ b/include/internal/catch_interfaces_runner.h @@ -18,6 +18,10 @@ namespace Catch { struct IRunner { virtual ~IRunner(); }; + + struct NullRunner : public IRunner + { + }; } #endif // TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED diff --git a/include/internal/catch_runner_impl.hpp b/include/internal/catch_runner_impl.hpp index eff066f6..20694851 100644 --- a/include/internal/catch_runner_impl.hpp +++ b/include/internal/catch_runner_impl.hpp @@ -48,7 +48,6 @@ namespace Catch { }; /////////////////////////////////////////////////////////////////////////// - class RunContext : public IResultCapture, public IRunner { RunContext( RunContext const& ); @@ -105,18 +104,16 @@ namespace Catch { } Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; std::string redirectedCout; std::string redirectedCerr; TestCaseInfo testInfo = testCase.getTestCaseInfo(); - m_reporter->testCaseStarting( testInfo ); + UnwindTestCaseOnCompletion finaliser(*this, m_totals, m_reporter, testInfo, redirectedCout, redirectedCerr); m_activeTestCase = &testCase; m_testCaseTracker = TestCaseTracker( testInfo.name ); - do { do { runCurrentTest( redirectedCout, redirectedCerr ); @@ -125,18 +122,10 @@ namespace Catch { } while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); - Totals deltaTotals = m_totals.delta( prevTotals ); - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); - m_activeTestCase = NULL; m_testCaseTracker.reset(); - return deltaTotals; + return finaliser.report(); } Ptr config() const { @@ -194,12 +183,7 @@ namespace Catch { 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; - } - + void unwindSection(SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); @@ -209,6 +193,15 @@ namespace Catch { m_messages.clear(); } + virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { + if( std::uncaught_exception() ) { + m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); + return; + } + + unwindSection(info, prevAssertions, _durationInSeconds); + } + virtual void pushScopedMessage( MessageInfo const& message ) { m_messages.push_back( message ); } @@ -257,16 +250,13 @@ namespace Catch { void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.name, testCaseInfo.description, testCaseInfo.lineInfo ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; + + UnwindSectionOnCompletion finaliser(*this, m_totals, m_reporter, testCaseInfo, m_unfinishedSections, m_messages); try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); TestCaseTracker::Guard guard( *m_testCaseTracker ); - Timer timer; - timer.start(); + finaliser.startTimer(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( std::cout, redirectedCout ); StreamRedirect cerrRedir( std::cerr, redirectedCerr ); @@ -275,38 +265,16 @@ namespace Catch { else { m_activeTestCase->invoke(); } - duration = timer.getElapsedSeconds(); + finaliser.stopTimer(); } -#ifdef INTERNAL_CATCH_VS_MANAGED // detect CLR - catch(AssertFailedException^) { - if( aborting() ) - throw; // CLR always rethrows - stop on first assert - } -#else - catch( INTERNAL_CATCH_TEST_FAILURE_EXCEPTION ) { + catch( const Catch::TestFailureException& ) { // This just means the test was aborted due to failure } -#endif catch(...) { ExpressionResultBuilder exResult( ResultWas::ThrewException ); exResult << translateActiveException(); actOnCurrentResult( exResult.buildResult( m_lastAssertionInfo ) ); } - // 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::const_iterator it = m_unfinishedSections.begin(), - itEnd = m_unfinishedSections.end(); - it != itEnd; - ++it ) - sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); - m_unfinishedSections.clear(); - m_messages.clear(); - - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); } private: @@ -320,6 +288,111 @@ namespace Catch { double durationInSeconds; }; + class UnwindSectionOnCompletion + { + public: + UnwindSectionOnCompletion(RunContext& context, Totals& totals, Ptr& reporter, TestCaseInfo const& testCaseInfo, + std::vector& unfinishedSections, std::vector& messages) + : m_context(context) + , m_totals(totals) + , m_reporter(reporter) + , m_testCaseSection( testCaseInfo.name, testCaseInfo.description, testCaseInfo.lineInfo ) + , m_unfinishedSections(unfinishedSections) + , m_messages(messages) + , m_duration(0.0) + { + m_prevAssertions = m_totals.assertions; + m_reporter->sectionStarting( m_testCaseSection ); + } + ~UnwindSectionOnCompletion() + { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down. + for( std::vector::const_iterator it = m_unfinishedSections.begin(), + itEnd = m_unfinishedSections.end(); + it != itEnd; + ++it ) { + m_context.unwindSection( it->info, it->prevAssertions, it->durationInSeconds ); + } + m_unfinishedSections.clear(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - m_prevAssertions; + bool missingAssertions = m_context.testForMissingAssertions( assertions ); + + SectionStats testCaseSectionStats( m_testCaseSection, assertions, m_duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); + } + void startTimer() + { + m_timer.start(); + } + void stopTimer() + { + m_duration = m_timer.getElapsedSeconds(); + } + private: + // non-copyable + UnwindSectionOnCompletion(const UnwindSectionOnCompletion&); + UnwindSectionOnCompletion& operator=(const UnwindSectionOnCompletion&); + + RunContext& m_context; + Totals& m_totals; + Ptr& m_reporter; + SectionInfo m_testCaseSection; + std::vector& m_unfinishedSections; + std::vector& m_messages; + Timer m_timer; + Counts m_prevAssertions; + double m_duration; + }; + + class UnwindTestCaseOnCompletion + { + public: + UnwindTestCaseOnCompletion(RunContext& context, Totals& totals, Ptr& reporter, TestCaseInfo& testInfo, + std::string& redirectedCout, std::string& redirectedCerr) + : m_context(context), m_totals(totals), m_reporter(reporter), m_testInfo(testInfo) + , m_redirectedCout(redirectedCout), m_redirectedCerr(redirectedCerr) + , m_reported(false) + { + m_prevTotals = m_totals; + m_reporter->testCaseStarting( m_testInfo ); + } + ~UnwindTestCaseOnCompletion() + { + if( !m_reported ) + { + report(); + } + } + Totals report() + { + m_reported = true; + Totals deltaTotals = m_totals.delta( m_prevTotals ); + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( m_testInfo, + deltaTotals, + m_redirectedCout, + m_redirectedCerr, + m_context.aborting() ) ); + return deltaTotals; + } + private: + // non-copyable + UnwindTestCaseOnCompletion(const UnwindTestCaseOnCompletion&); + UnwindTestCaseOnCompletion& operator=(const UnwindTestCaseOnCompletion&); + + RunContext& m_context; + Totals& m_totals; + Ptr& m_reporter; + TestCaseInfo& m_testInfo; + std::string& m_redirectedCout; + std::string& m_redirectedCerr; + bool m_reported; + Totals m_prevTotals; + }; + TestRunInfo m_runInfo; IMutableContext& m_context; TestCase const* m_activeTestCase; diff --git a/include/internal/catch_timer.hpp b/include/internal/catch_timer.hpp index 141c1def..eaf396e8 100644 --- a/include/internal/catch_timer.hpp +++ b/include/internal/catch_timer.hpp @@ -24,15 +24,25 @@ namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS + template + struct CounterDefaults + { + static T hz; + static T hzo; + }; + template + T CounterDefaults::hz = 0; + template + T CounterDefaults::hzo = 0; + INTERNAL_CATCH_INLINE uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; - if (!hz) { - QueryPerformanceFrequency((LARGE_INTEGER*)&hz); - QueryPerformanceCounter((LARGE_INTEGER*)&hzo); + if (!CounterDefaults::hz) { + QueryPerformanceFrequency((LARGE_INTEGER*)&CounterDefaults::hz); + QueryPerformanceCounter((LARGE_INTEGER*)&CounterDefaults::hzo); } uint64_t t; QueryPerformanceCounter((LARGE_INTEGER*)&t); - return ((t-hzo)*1000000)/hz; + return ((t-CounterDefaults::hzo)*1000000)/CounterDefaults::hz; } #else INTERNAL_CATCH_INLINE uint64_t getCurrentTicks() { diff --git a/include/internal/catch_tostring.hpp b/include/internal/catch_tostring.hpp index cec4cfed..5e098aa6 100644 --- a/include/internal/catch_tostring.hpp +++ b/include/internal/catch_tostring.hpp @@ -95,7 +95,11 @@ struct StringMaker { if( !p ) return INTERNAL_CATCH_STRINGIFY( NULL ); std::ostringstream oss; +#ifdef _MSC_VER + oss << "0x" << p; +#else oss << p; +#endif return oss.str(); } }; diff --git a/include/internal/catch_vs_managed_impl.hpp b/include/internal/catch_vs_managed_impl.hpp index 7991091f..7edeaf13 100644 --- a/include/internal/catch_vs_managed_impl.hpp +++ b/include/internal/catch_vs_managed_impl.hpp @@ -18,7 +18,7 @@ using namespace System::Collections::Generic; using namespace Microsoft::VisualStudio::TestTools::UnitTesting; namespace Catch { - inline String^ convert_string_to_managed(const std::string& s) + inline String^ convert_string_for_assert(const std::string& s) { String^ result = gcnew String(s.c_str()); return result; @@ -26,71 +26,12 @@ namespace Catch { } -#include "internal/catch_timer.hpp" #include "internal/catch_reporter_registrars.hpp" #include "reporters/catch_vs_reporter.hpp" #include "catch_registry_hub.hpp" - -//#define OLD (1) -#ifdef OLD -namespace Catch { - - class ExceptionRegistryHub : public IRegistryHub, public IMutableRegistryHub { - - ExceptionRegistryHub( ExceptionRegistryHub const& ); - void operator=( ExceptionRegistryHub const& ); - - public: // IRegistryHub - ExceptionRegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { - return m_exceptionTranslatorRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const&, IReporterFactory* ) { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual void registerTest( TestCase const& ) { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - - private: - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; - - template - struct GlobalRegistryHub - { - static T*& instance() - { - if( !theRegistryHub ) - theRegistryHub = new T(); - return theRegistryHub; - } - static T* theRegistryHub; - }; - template - T* GlobalRegistryHub::theRegistryHub = NULL; - - INTERNAL_CATCH_INLINE IMutableRegistryHub& getMutableRegistryHub() { - return *GlobalRegistryHub::instance(); - } - INTERNAL_CATCH_INLINE std::string translateActiveException() { - return GlobalRegistryHub::instance()->getExceptionTranslatorRegistry().translateActiveException(); - } - -} -#endif // OLD +#include "internal/catch_timer.hpp" +#include "internal/catch_console_colour_impl.hpp" +#include "catch_runner.hpp" namespace Catch { inline NonCopyable::~NonCopyable() {} @@ -113,16 +54,10 @@ namespace Catch { inline TestCaseStats::~TestCaseStats() {} inline TestGroupStats::~TestGroupStats() {} inline TestRunStats::~TestRunStats() {} - //CumulativeReporterBase::SectionNode::~SectionNode() {} - //CumulativeReporterBase::~CumulativeReporterBase() {} - //StreamingReporterBase::~StreamingReporterBase() {} - //ConsoleReporter::~ConsoleReporter() {} inline IRunner::~IRunner() {} inline IMutableContext::~IMutableContext() {} inline IConfig::~IConfig() {} - //XmlReporter::~XmlReporter() {} - //JunitReporter::~JunitReporter() {} inline TestRegistry::~TestRegistry() {} inline FreeFunctionTestCase::~FreeFunctionTestCase() {} inline IGeneratorInfo::~IGeneratorInfo() {} @@ -137,8 +72,6 @@ namespace Catch { inline Matchers::Impl::StdString::EndsWith::~EndsWith() {} inline void Config::dummy() {} - - //INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter ) } #endif diff --git a/include/internal/catch_vs_native_impl.hpp b/include/internal/catch_vs_native_impl.hpp index f0c2e242..dbb1e56a 100644 --- a/include/internal/catch_vs_native_impl.hpp +++ b/include/internal/catch_vs_native_impl.hpp @@ -16,71 +16,55 @@ using Microsoft::VisualStudio::CppUnitTestFramework::Logger; using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using Microsoft::VisualStudio::CppUnitTestFramework::__LineInfo; -#define INTERNAL_CATCH_INLINE inline - #include #include -#include "internal/catch_timer.hpp" -#include "internal/catch_vs_test_registry.hpp" +#include "internal/catch_reporter_registrars.hpp" #include "reporters/catch_vs_reporter.hpp" +#include "catch_registry_hub.hpp" +#include "internal/catch_timer.hpp" +#include "internal/catch_console_colour_impl.hpp" +#include "catch_runner.hpp" namespace Catch { + inline NonCopyable::~NonCopyable() {} + inline IShared::~IShared() {} + inline StreamBufBase::~StreamBufBase() throw() {} + inline IContext::~IContext() {} + inline IResultCapture::~IResultCapture() {} + inline ITestCase::~ITestCase() {} + inline ITestCaseRegistry::~ITestCaseRegistry() {} + inline IRegistryHub::~IRegistryHub() {} + inline IMutableRegistryHub::~IMutableRegistryHub() {} + inline IExceptionTranslator::~IExceptionTranslator() {} + inline IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + inline IReporter::~IReporter() {} + inline IReporterFactory::~IReporterFactory() {} + inline IReporterRegistry::~IReporterRegistry() {} + inline IStreamingReporter::~IStreamingReporter() {} + inline AssertionStats::~AssertionStats() {} + inline SectionStats::~SectionStats() {} + inline TestCaseStats::~TestCaseStats() {} + inline TestGroupStats::~TestGroupStats() {} + inline TestRunStats::~TestRunStats() {} - class ExceptionRegistryHub : public IRegistryHub, public IMutableRegistryHub { + inline IRunner::~IRunner() {} + inline IMutableContext::~IMutableContext() {} + inline IConfig::~IConfig() {} + inline TestRegistry::~TestRegistry() {} + inline FreeFunctionTestCase::~FreeFunctionTestCase() {} + inline IGeneratorInfo::~IGeneratorInfo() {} + inline IGeneratorsForTest::~IGeneratorsForTest() {} + inline TagParser::~TagParser() {} + inline TagExtracter::~TagExtracter() {} + inline TagExpressionParser::~TagExpressionParser() {} - ExceptionRegistryHub( ExceptionRegistryHub const& ); - void operator=( ExceptionRegistryHub const& ); - - public: // IRegistryHub - ExceptionRegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { - return m_exceptionTranslatorRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const&, IReporterFactory* ) { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual void registerTest( TestCase const& ) { - throw std::runtime_error("can't do this for Visual Studio tests!"); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - - private: - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; - - template - struct GlobalRegistryHub - { - static T& instance() - { - if( !theRegistryHub ) - theRegistryHub = new T(); - return *theRegistryHub; - } - static T* theRegistryHub; - }; - template - T* GlobalRegistryHub::theRegistryHub = NULL; - - INTERNAL_CATCH_INLINE IMutableRegistryHub& getMutableRegistryHub() { - return GlobalRegistryHub::instance(); - } - INTERNAL_CATCH_INLINE std::string translateActiveException() { - return GlobalRegistryHub::instance().getExceptionTranslatorRegistry().translateActiveException(); - } + inline Matchers::Impl::StdString::Equals::~Equals() {} + inline Matchers::Impl::StdString::Contains::~Contains() {} + inline Matchers::Impl::StdString::StartsWith::~StartsWith() {} + inline Matchers::Impl::StdString::EndsWith::~EndsWith() {} + inline void Config::dummy() {} } #endif // INTERNAL_CATCH_VS_NATIVE diff --git a/include/internal/catch_vs_test_registry.hpp b/include/internal/catch_vs_test_registry.hpp index dfc09c20..6071ac69 100644 --- a/include/internal/catch_vs_test_registry.hpp +++ b/include/internal/catch_vs_test_registry.hpp @@ -63,7 +63,7 @@ private: typedef void(*TestFunction)(); struct NameAndDesc { -#if (_MANAGED == 1) || (_M_CEE == 1) // detect CLR +#ifdef INTERNAL_CATCH_VS_MANAGED NameAndDesc( const char* _name = "", const char* _description= "" ) : name( _name ), description( _description ) {} @@ -74,14 +74,35 @@ struct NameAndDesc { NameAndDesc( const wchar_t* _name, const char* _description= "" ) : name(), description( _description ) { - stdext::cvt::wstring_convert > conv1; - name = conv1.to_bytes(_name); + assignName(_name); } NameAndDesc( const wchar_t* _name, int ) : name(), description( "" ) + { + assignName(_name); + } + void assignName(const wchar_t* _name) { stdext::cvt::wstring_convert > conv1; - name = conv1.to_bytes(_name); + std::string tmp = conv1.to_bytes(_name); + if( tmp.empty() ) + { + name = tmp; + } + else + { + std::string::iterator startIter = tmp.begin(); + if(*startIter == '\"') + { + ++startIter; + } + std::string::reverse_iterator endIter = tmp.rbegin(); + if(*endIter == '\"') + { + ++endIter; + } + name.assign(startIter, endIter.base()); + } } #endif @@ -120,7 +141,7 @@ private: } // end namespace Catch -#if (_MANAGED == 1) || (_M_CEE == 1) // detect CLR +#ifdef INTERNAL_CATCH_VS_MANAGED #define CATCH_INTERNAL_HANDLE_EMPTY_PARAM2( name ) name##"" #define CATCH_INTERNAL_HANDLE_EMPTY_PARAM(...) CATCH_INTERNAL_HANDLE_EMPTY_PARAM2( INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__) ) @@ -147,7 +168,7 @@ private: #define CATCH_INTERNAL_NAMESPACE( Ext ) -#define INTERNAL_CATCH_TEST_METHOD( Method, UniqueExt, Name, Desc ) \ +#define INTERNAL_CATCH_TEST_METHOD( Count, UniqueExt, Name, Desc ) \ public: \ [TestMethod] \ [Description( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name) )] \ @@ -155,22 +176,24 @@ private: void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) () \ { \ Catch::NameAndDesc name_desc( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name), Desc ); \ - CATCH_INTERNAL_RUN_SINGLE_TEST( Method ); \ + CATCH_INTERNAL_RUN_SINGLE_TEST(Count); \ } -#define INTERNAL_CATCH_TEST_CLASS_METHOD( Method, UniqueExt, Name, Desc ) \ +#define BEGIN_INTERNAL_CATCH_BATCH_METHOD( Tags, UniqueExt ) \ public: \ [TestMethod] \ - [Description( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name) )] \ - [TestProperty( "Description", CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name) )] \ - void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) () \ - { \ - Catch::NameAndDesc name_desc( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name), Desc ); \ - CATCH_INTERNAL_RUN_SINGLE_CLASS_TEST( Method ); \ - } + [TestCategory( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Tags) )] \ + [Description( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Tags) )] \ + [TestProperty( "Description", CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Tags) )] \ + void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) () #define CHECK_FOR_TEST_CASE_CLASH +#define INTERNAL_CATCH_MAP_CATEGORY_TO_TAG( Category, Tag ) \ + INTERNAL_CATCH_MAP_CATEGORY_TO_TAG2( #Category, Tag, __COUNTER__ ) + +#define FAIL_STRING( str ) _T( str ) + #else // detect CLR // Native tests @@ -186,108 +209,161 @@ private: #define TEST_IMPL_2(tuple) TEST_IMPL2 tuple #define TEST_IMPL2( INTERNAL_CATCH_SPLIT_ARG_1,INTERNAL_CATCH_SPLIT_ARG_2,N,...) L#INTERNAL_CATCH_SPLIT_ARG_1 -#define CATCH_INTERNAL_HANDLE_EMPTY_PARAM(...) CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPL( (__VA_ARGS__, 2, 1) ) -#define CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPL(tuple) CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPL2 tuple -#define CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPL2( INTERNAL_CATCH_SPLIT_ARG_1,INTERNAL_CATCH_SPLIT_ARG_2,N,...) #INTERNAL_CATCH_SPLIT_ARG_1 -#define CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(...) CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPLW( (__VA_ARGS__, 2, 1) ) +#define CATCH_INTERNAL_HANDLE_EMPTY_PARAM(...) CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPLW( (__VA_ARGS__, 2, 1) ) #define CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPLW(tuple) CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPL2W tuple #define CATCH_INTERNAL_HANDLE_EMPTY_PARAM_IMPL2W( INTERNAL_CATCH_SPLIT_ARG_1,INTERNAL_CATCH_SPLIT_ARG_2,N,...) L#INTERNAL_CATCH_SPLIT_ARG_1 -#define INTERNAL_CATCH_TEST_METHOD( Method, UniqueExt, Name, Desc ) \ +#define INTERNAL_CATCH_TEST_METHOD( Count, UniqueExt, Name, Desc ) \ public: \ BEGIN_TEST_METHOD_ATTRIBUTE( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) ) \ - TEST_OWNER( CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(Name) ) \ - TEST_DESCRIPTION( CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(Name) ) \ + TEST_OWNER( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name) ) \ + TEST_DESCRIPTION( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name) ) \ END_TEST_METHOD_ATTRIBUTE() \ TEST_METHOD( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) ) \ { \ - Catch::NameAndDesc name_desc(CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(Name), Desc ); \ - CATCH_INTERNAL_RUN_SINGLE_TEST( Method ); \ + Catch::NameAndDesc name_desc(CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name), Desc ); \ + CATCH_INTERNAL_RUN_SINGLE_TEST(Count); \ } -#define INTERNAL_CATCH_TEST_CLASS_METHOD( Method, UniqueExt, Name, Desc ) \ +#define BEGIN_INTERNAL_CATCH_BATCH_METHOD( Tags, UniqueExt ) \ public: \ - BEGIN_TEST_METHOD_ATTRIBUTE( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) ) \ - TEST_OWNER( CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(Name) ) \ - TEST_DESCRIPTION( CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(Name) ) \ - END_TEST_METHOD_ATTRIBUTE() \ - TEST_METHOD( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) ) \ - { \ - Catch::NameAndDesc name_desc( CATCH_INTERNAL_HANDLE_EMPTY_PARAMW(Name), Desc ); \ - CATCH_INTERNAL_RUN_SINGLE_CLASS_TEST( Method ); \ - } + BEGIN_TEST_METHOD_ATTRIBUTE( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) ) \ + TEST_OWNER( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Tags) ) \ + TEST_DESCRIPTION( CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Tags) ) \ + END_TEST_METHOD_ATTRIBUTE() \ + TEST_METHOD( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H___M_E_T_H_O_D___, UniqueExt) ) #define CHECK_FOR_TEST_CASE_CLASH void INTERNAL_CATCH_UNIQUE_NAME_LINE( if_you_get_this_error_you_have_a_test_case_name_clash_please_put_a_namespace_around_the_test_case_at_line_, __LINE__ )() {} +#define INTERNAL_CATCH_MAP_CATEGORY_TO_TAG( Category, Tag ) \ + INTERNAL_CATCH_MAP_CATEGORY_TO_TAG2( Category, Tag, __COUNTER__ ) + +#define FAIL_STRING( str ) WIDEN( str ) + #endif // detect CLR -#define INTERNAL_CATCH_CONCAT_LINE_COUNTER INTERNAL_CATCH_UNIQUE_NAME_LINE( INTERNAL_CATCH_UNIQUE_NAME_LINE( __LINE__, _ ), __COUNTER__ ) +#define INTERNAL_CATCH_CONCAT_LINE_COUNTER( count ) INTERNAL_CATCH_UNIQUE_NAME_LINE( INTERNAL_CATCH_UNIQUE_NAME_LINE( __LINE__, _ ), count ) -#define CATCH_INTERNAL_RUN_SINGLE_TEST( Method ) \ - { Catch::ConfigData cd; \ +#define CATCH_INTERNAL_CONFIG_SHOW_SUCCESS2( v, Count ) \ + namespace { CatchOverrides::ConfigShowSuccessfulTests INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H_____O_V_E_R_R_I_D_E____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(__FILE__, Count, v); } + +#define CATCH_INTERNAL_CONFIG_WARN_MISSING_ASSERTIONS2( v, Count ) \ + namespace { CatchOverrides::ConfigWarnMissingAssertions INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H_____O_V_E_R_R_I_D_E____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(__FILE__, Count, v); } + +#define CATCH_INTERNAL_CONFIG_SHOW_SUCCESS( v ) \ + CATCH_INTERNAL_CONFIG_SHOW_SUCCESS2( v, __COUNTER__) + +#define CATCH_INTERNAL_CONFIG_WARN_MISSING_ASSERTIONS( v ) \ + CATCH_INTERNAL_CONFIG_WARN_MISSING_ASSERTIONS2( v, __COUNTER__) + +#define CATCH_INTERNAL_RUN_SINGLE_TEST( Count ) \ + { CatchOverrides::ConfigGuard cg; \ + Catch::ConfigData cd(cg.value().get()); \ cd.name = name_desc.name; \ cd.abortAfter = 1; \ + cd.showSuccessfulTests = CatchOverrides::Config::instance().includeSuccessfulResults(__FILE__, Count ); \ + cd.warnings = (CatchOverrides::Config::instance().warnAboutMissingAssertions(__FILE__, Count ) ? Catch::WarnAbout::NoAssertions : Catch::WarnAbout::Nothing); \ Catch::Ptr config(new Catch::Config(cd)); \ Catch::MSTestReporter* rep = new Catch::MSTestReporter(config.get()); \ Catch::RunContext tr(config.get(), rep); \ std::vector testCase = Catch::getRegistryHub().getTestCaseRegistry().getMatchingTestCases(name_desc.name); \ - if( testCase.empty() ) Assert::Fail("No tests match"); \ - if( testCase.size() > 1 ) Assert::Fail("More than one test with the same name"); \ - tr.runTest(*testCase.begin()); \ + if( testCase.empty() ) Assert::Fail(FAIL_STRING("No tests match")); \ + if( testCase.size() > 1 ) Assert::Fail(FAIL_STRING("More than one test with the same name")); \ + Catch::Totals totals = tr.runTest(*testCase.begin()); \ + if( totals.assertions.failed > 0 ) { \ + INTERNAL_CATCH_TEST_THROW_FAILURE \ + } \ } -#define CATCH_INTERNAL_RUN_SINGLE_CLASS_TEST( ClassMethod ) \ - { Catch::ConfigData cd; \ - cd.name = name_desc.name; \ - cd.abortAfter = 1; \ - Catch::Ptr config(new Catch::Config(cd)); \ - Catch::MSTestReporter* rep = new Catch::MSTestReporter(config.get()); \ - Catch::RunContext tr(config.get(), rep); \ - std::vector testCase = Catch::getRegistryHub().getTestCaseRegistry().getMatchingTestCases(name_desc.name); \ - if( testCase.empty() ) Assert::Fail("No tests match"); \ - if( testCase.size() > 1 ) Assert::Fail("More than one test with the same name"); \ - tr.runTest(*testCase.begin()); \ - } - -#define INTERNAL_CATCH_TESTCASE2( UniqueExt, Name, Desc ) \ +#define INTERNAL_CATCH_TESTCASE2( Count, Name, Desc ) \ CHECK_FOR_TEST_CASE_CLASH \ - static void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt )(); \ - namespace CATCH_INTERNAL_NAMESPACE( UniqueExt ) { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( & INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(Name, Desc) ); \ - INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, UniqueExt ) ) \ + static void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(); \ + namespace CATCH_INTERNAL_NAMESPACE( INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( & INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name), Desc) ); \ + CatchOverrides::ConfigReset INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_O_N_F_I_G___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(__FILE__, Count); \ + INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) ) \ { \ INTERNAL_CATCH_CLASS_CONTEXT \ - INTERNAL_CATCH_TEST_METHOD( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt ), UniqueExt, Name, Desc ) \ + INTERNAL_CATCH_TEST_METHOD( Count, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ), Name, Desc ) \ }; \ } \ - void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt )() + void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )() -#define INTERNAL_CATCH_METHOD_AS_TEST_CASE2( QualifiedMethod, UniqueExt, Name, Desc ) \ +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE2( QualifiedMethod, Count, Name, Desc ) \ CHECK_FOR_TEST_CASE_CLASH \ - namespace CATCH_INTERNAL_NAMESPACE( UniqueExt ) { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( & QualifiedMethod, "&" # QualifiedMethod, Catch::NameAndDesc(Name, Desc), CATCH_INTERNAL_LINEINFO ); \ - INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, UniqueExt ) ) \ + namespace CATCH_INTERNAL_NAMESPACE( INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( & QualifiedMethod, "&" # QualifiedMethod, Catch::NameAndDesc(CATCH_INTERNAL_HANDLE_EMPTY_PARAM(Name), Desc), CATCH_INTERNAL_LINEINFO ); \ + CatchOverrides::ConfigReset INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_O_N_F_I_G___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(__FILE__, Count); \ + INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) ) \ { \ INTERNAL_CATCH_CLASS_CONTEXT \ - INTERNAL_CATCH_TEST_CLASS_METHOD( QualifiedMethod, UniqueExt, Name, Desc ) \ + INTERNAL_CATCH_TEST_METHOD( Count, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ), Name, Desc ) \ }; \ }; -#define INTERNAL_CATCH_TEST_CASE_METHOD2( ClassName, UniqueExt, TestName, Desc ) \ +#define INTERNAL_CATCH_TEST_CASE_METHOD2( ClassName, Count, TestName, Desc ) \ CHECK_FOR_TEST_CASE_CLASH \ - struct INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt ) : ClassName { \ + struct INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) : ClassName { \ void test(); \ - static void invoke() { INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt ) tmp; tmp.test(); } \ + static void invoke() { INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) tmp; tmp.test(); } \ }; \ - namespace CATCH_INTERNAL_NAMESPACE( UniqueExt ) { \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( & INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt )::invoke, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(TestName, Desc) ); \ - INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, UniqueExt ) ) \ + namespace CATCH_INTERNAL_NAMESPACE( INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( & INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )::invoke, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc(CATCH_INTERNAL_HANDLE_EMPTY_PARAM(TestName), Desc) ); \ + CatchOverrides::ConfigReset INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_O_N_F_I_G___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(__FILE__, Count); \ + INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) ) \ { \ INTERNAL_CATCH_CLASS_CONTEXT \ - INTERNAL_CATCH_TEST_METHOD( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt )::invoke, UniqueExt, TestName, Desc ) \ + INTERNAL_CATCH_TEST_METHOD( Count, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ), TestName, Desc ) \ }; \ } \ - void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, UniqueExt )::test() + void INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )::test() + +#if defined(INTERNAL_CATCH_VS_MANAGED) + + #define INTERNAL_CATCH_TEST_REPORT_BATCH_FAILURE( count ) \ + { \ + std::stringstream _sf; \ + _sf << count << " assertions failed - check output for results."; \ + std::string fail = _sf.str(); \ + Assert::Fail(Catch::convert_string_for_assert(fail)); \ + } + +#else + + #define INTERNAL_CATCH_TEST_REPORT_BATCH_FAILURE( count ) \ + { \ + std::wstringstream _s; \ + _s << count << " assertions failed - check output for results."; \ + std::wstring ws = _s.str(); \ + Assert::Fail(ws.c_str()); \ + } +#endif + +#define INTERNAL_CATCH_MAP_CATEGORY_TO_TAG2( Category, Tag, Count ) \ + CHECK_FOR_TEST_CASE_CLASH \ + namespace CATCH_INTERNAL_NAMESPACE( INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) { \ + CatchOverrides::ConfigReset INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_O_N_F_I_G___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) )(__FILE__, Count); \ + INTERNAL_CATCH_CLASS_DEFINITION( INTERNAL_CATCH_UNIQUE_NAME_LINE( C_A_T_C_H____T_E_S_T____C_L_A_S_S___, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) ) \ + { \ + INTERNAL_CATCH_CLASS_CONTEXT \ + BEGIN_INTERNAL_CATCH_BATCH_METHOD( Category, INTERNAL_CATCH_CONCAT_LINE_COUNTER( Count ) ) \ + { \ + Catch::ConfigData cd; \ + cd.showSuccessfulTests = CatchOverrides::Config::instance().includeSuccessfulResults(__FILE__, Count ); \ + cd.warnings = (CatchOverrides::Config::instance().warnAboutMissingAssertions(__FILE__, Count ) ? Catch::WarnAbout::NoAssertions : Catch::WarnAbout::Nothing); \ + cd.reporterName = "vs_reporter"; \ + cd.name = "Batch run using tag : " Tag; \ + cd.testsOrTags.push_back( Tag ); \ + Catch::Ptr config(new Catch::Config(cd)); \ + Catch::ReporterRegistrar reporterReg("vs_reporter"); \ + Catch::Runner runner(config); \ + Catch::Totals totals = runner.runTests(); \ + if( totals.assertions.failed > 0 ) { \ + INTERNAL_CATCH_TEST_REPORT_BATCH_FAILURE(totals.assertions.failed) \ + } \ + } \ + }; \ + } //#undef CATCH_CONFIG_VARIADIC_MACROS @@ -301,29 +377,29 @@ private: #define INTERNAL_CATCH_SPLIT_TAGS_IMPL_(INTERNAL_CATCH_SPLIT_ARG_1,INTERNAL_CATCH_SPLIT_ARG_2,N,...) INTERNAL_CATCH_SPLIT_ARG_2 #define INTERNAL_CATCH_TESTCASE( ... ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_CONCAT_LINE_COUNTER, INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__), INTERNAL_CATCH_SPLIT_TAGS(__VA_ARGS__) ) + INTERNAL_CATCH_TESTCASE2( __COUNTER__ , INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__), INTERNAL_CATCH_SPLIT_TAGS(__VA_ARGS__) ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - INTERNAL_CATCH_METHOD_AS_TEST_CASE2( QualifiedMethod, INTERNAL_CATCH_CONCAT_LINE_COUNTER, INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__), INTERNAL_CATCH_SPLIT_TAGS(__VA_ARGS__) ) + INTERNAL_CATCH_METHOD_AS_TEST_CASE2( QualifiedMethod, __COUNTER__, INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__), INTERNAL_CATCH_SPLIT_TAGS(__VA_ARGS__) ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ - INTERNAL_CATCH_TEST_CASE_METHOD2(ClassName, INTERNAL_CATCH_CONCAT_LINE_COUNTER, INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__), INTERNAL_CATCH_SPLIT_TAGS(__VA_ARGS__) ) + INTERNAL_CATCH_TEST_CASE_METHOD2(ClassName, __COUNTER__, INTERNAL_CATCH_SPLIT_ARGS_2(__VA_ARGS__), INTERNAL_CATCH_SPLIT_TAGS(__VA_ARGS__) ) #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_CONCAT_LINE_COUNTER, Name, Desc ) + INTERNAL_CATCH_TESTCASE2( __COUNTER__ , Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - INTERNAL_CATCH_METHOD_AS_TEST_CASE2( QualifiedMethod, INTERNAL_CATCH_CONCAT_LINE_COUNTER, Name, Desc ) + INTERNAL_CATCH_METHOD_AS_TEST_CASE2( QualifiedMethod, __COUNTER__, Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2(ClassName, INTERNAL_CATCH_CONCAT_LINE_COUNTER, TestName, Desc ) + INTERNAL_CATCH_TEST_CASE_METHOD2(ClassName, __COUNTER__, TestName, Desc ) #endif diff --git a/include/reporters/catch_vs_reporter.hpp b/include/reporters/catch_vs_reporter.hpp index 0f9c9966..9b7d2368 100644 --- a/include/reporters/catch_vs_reporter.hpp +++ b/include/reporters/catch_vs_reporter.hpp @@ -49,18 +49,24 @@ namespace Catch { MSTestReporter( ReporterConfig const& _config ) : m_config( _config.fullConfig() ), m_headerPrinted( false ), - m_atLeastOneTestCasePrinted( false ) + m_atLeastOneTestCasePrinted( false ), + m_failed(0) {} MSTestReporter( Ptr const& _fullConfig ) : m_config( _fullConfig ), m_headerPrinted( false ), - m_atLeastOneTestCasePrinted( false ) + m_atLeastOneTestCasePrinted( false ), + m_failed(0) {} virtual ~MSTestReporter() { if( m_atLeastOneTestCasePrinted ) { write_output_message(stream.str()); + /*if( m_failed ) + { + Assert::IsTrue(false, L"At least one test failed - examine output for failures."); + }*/ } } @@ -126,9 +132,6 @@ namespace Catch { write_output_message(_testCaseStats.stdErr); write_output_message(getDoubleDashes()); } - if( _testCaseStats.totals.assertions.failed ) { - Assert::IsTrue(false, L"At least one test failed - examine output for CHECK failures."); - } m_headerPrinted = false; currentTestCaseInfo.reset(); assert( m_sectionStack.empty() ); @@ -147,6 +150,7 @@ namespace Catch { printTotalsDivider(); printTotals( _testRunStats.totals ); stream << "\r\n" << "\r\n"; + m_failed = _testRunStats.totals.testCases.failed; currentTestCaseInfo.reset(); currentGroupInfo.reset(); currentTestRunInfo.reset(); @@ -454,6 +458,7 @@ namespace Catch { std::vector m_sectionStack; bool m_headerPrinted; bool m_atLeastOneTestCasePrinted; + size_t m_failed; }; } // end namespace Catch diff --git a/projects/SelfTest/RunAllTests.cpp b/projects/SelfTest/RunAllTests.cpp new file mode 100644 index 00000000..eca4e0bc --- /dev/null +++ b/projects/SelfTest/RunAllTests.cpp @@ -0,0 +1,187 @@ +/* + * Created by Phil on 22/10/2010. + * Copyright 2010 Two Blue Cubes Ltd + * + * 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) + */ + +#include "catch.hpp" +#include "internal/catch_text.h" +#include "internal/catch_console_colour.hpp" + +namespace AllTestsRunner { + + class NullStreamingReporter : public Catch::SharedImpl { + public: + + virtual ~NullStreamingReporter(); + + static std::string getDescription() { + return "null reporter"; + } + + private: // IStreamingReporter + + virtual Catch::ReporterPreferences getPreferences() const { + return Catch::ReporterPreferences(); + } + + virtual void noMatchingTestCases( std::string const& ) {} + virtual void testRunStarting( Catch::TestRunInfo const& ) {} + virtual void testGroupStarting( Catch::GroupInfo const& ) {} + virtual void testCaseStarting( Catch::TestCaseInfo const& ) {} + virtual void sectionStarting( Catch::SectionInfo const& ) {} + virtual void assertionStarting( Catch::AssertionInfo const& ) {} + virtual bool assertionEnded( Catch::AssertionStats const& ) { return false; } + virtual void sectionEnded( Catch::SectionStats const& ) {} + virtual void testCaseEnded( Catch::TestCaseStats const& ) {} + virtual void testGroupEnded( Catch::TestGroupStats const& ) {} + virtual void testRunEnded( Catch::TestRunStats const& ) {} + }; + + class EmbeddedRunner { + + public: + EmbeddedRunner() : m_reporter( new NullStreamingReporter() ) {} + + Catch::Totals runMatching( const std::string& rawTestSpec, + std::size_t groupIndex, + std::size_t groupsCount, + const std::string& reporter = "console" ); + + private: + Catch::Ptr m_reporter; + }; + + class MetaTestRunner { + + public: + struct Expected { enum Result { + ToSucceed, + ToFail + }; }; + + MetaTestRunner( Expected::Result expectedResult, std::size_t groupIndex, std::size_t groupsCount ) + : m_expectedResult( expectedResult ), + m_groupIndex( groupIndex ), + m_groupsCount( groupsCount ) + {} + + static void runMatching( const std::string& testSpec, + Expected::Result expectedResult, + std::size_t groupIndex, + std::size_t groupsCount ) { + forEach( Catch::getRegistryHub().getTestCaseRegistry().getMatchingTestCases( testSpec ), + MetaTestRunner( expectedResult, groupIndex, groupsCount ) ); + } + + void operator()( const Catch::TestCase& testCase ) { + std::string name; + Catch::Totals totals; + { + EmbeddedRunner runner; + name = testCase.getTestCaseInfo().name; + totals = runner.runMatching( name, m_groupIndex, m_groupsCount ); + } + switch( m_expectedResult ) { + case Expected::ToSucceed: + if( totals.assertions.failed > 0 ) { + FAIL( "Expected test case '" + << name + << "' to succeed but there was/ were " + << totals.assertions.failed << " failure(s)" ); + } + else { + SUCCEED( "Tests passed, as expected" ); + } + break; + case Expected::ToFail: + if( totals.assertions.failed == 0 ) { + FAIL( "Expected test case '" + << name + << "' to fail but there was/ were " + << totals.assertions.passed << " success(es)" ); + } + else { + SUCCEED( "Tests failed, as expected" ); + } + break; + } + } + + private: + Expected::Result m_expectedResult; + std::size_t m_groupIndex; + std::size_t m_groupsCount; + }; + + NullStreamingReporter::~NullStreamingReporter() {} + + Catch::Totals EmbeddedRunner::runMatching( const std::string& rawTestSpec, std::size_t groupIndex, std::size_t groupsCount, const std::string& ) { + std::ostringstream oss; + Catch::Ptr config = new Catch::Config(); + config->setStreamBuf( oss.rdbuf() ); + + Catch::Totals totals; + + // Scoped because RunContext doesn't report EndTesting until its destructor + { + Catch::RunContext runner( config.get(), m_reporter.get() ); + totals = runner.runMatching( rawTestSpec, groupIndex, groupsCount ); + } + return totals; + } + + TEST_CASE( "Run all failing and succeeding tests", "[vsall]" ) { + + /////////////////////////////////////////////////////////////////////////// + SECTION( "selftest/expected result", + "Tests do what they claim" ) { + +#ifdef _UNICODE + std::cout << "using Unicode..." << std::endl; +#else + std::cout << "using Mbcs..." << std::endl; +#endif + + SECTION( "selftest/expected result/failing tests", + "Tests in the 'failing' branch fail" ) { + std::cout << "Tests in the 'failing' branch fail" << std::endl; + MetaTestRunner::runMatching( "./failing/*", MetaTestRunner::Expected::ToFail, 0, 2 ); + } + + SECTION( "selftest/expected result/succeeding tests", + "Tests in the 'succeeding' branch succeed" ) { + std::cout << "Tests in the 'succeeding' branch succeed" << std::endl; + MetaTestRunner::runMatching( "./succeeding/*", MetaTestRunner::Expected::ToSucceed, 1, 2 ); + } + } + + /////////////////////////////////////////////////////////////////////////// + SECTION( "selftest/test counts", + "Number of test cases that run is fixed" ) { + EmbeddedRunner runner; + + SECTION( "selftest/test counts/succeeding tests", + "Number of 'succeeding' tests is fixed" ) { + std::cout << "Number of 'succeeding' tests is fixed" << std::endl; + Catch::Totals totals = runner.runMatching( "./succeeding/*", 0, 2 ); + CHECK( totals.assertions.passed == 298 ); + CHECK( totals.assertions.failed == 0 ); + } + + SECTION( "selftest/test counts/failing tests", + "Number of 'failing' tests is fixed" ) { + std::cout << "Number of 'failing' tests is fixed" << std::endl; + Catch::Totals totals = runner.runMatching( "./failing/*", 1, 2 ); + CHECK( totals.assertions.passed == 2 ); + CHECK( totals.assertions.failed == 77 ); + } + } + } + +#if defined(INTERNAL_CATCH_VS_MANAGED) || defined(INTERNAL_CATCH_VS_NATIVE) + CATCH_MAP_CATEGORY_TO_TAG(all, "[vsall]"); +#endif +} diff --git a/projects/SelfTest/TestMain.cpp b/projects/SelfTest/TestMain.cpp index dc2684ed..f793ce6b 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -13,53 +13,55 @@ #include "internal/catch_text.h" #include "internal/catch_console_colour.hpp" -TEST_CASE( "selftest/main", "Runs all Catch self tests and checks their results" ) { - using namespace Catch; +namespace TestMain { - /////////////////////////////////////////////////////////////////////////// - SECTION( "selftest/expected result", - "Tests do what they claim" ) { + TEST_CASE( "selftest/main", "Runs all Catch self tests and checks their results" ) { + using namespace Catch; + + /////////////////////////////////////////////////////////////////////////// + SECTION( "selftest/expected result", + "Tests do what they claim" ) { - SECTION( "selftest/expected result/failing tests", - "Tests in the 'failing' branch fail" ) { - MetaTestRunner::runMatching( "./failing/*", MetaTestRunner::Expected::ToFail, 0, 2 ); - } + SECTION( "selftest/expected result/failing tests", + "Tests in the 'failing' branch fail" ) { + MetaTestRunner::runMatching( "./failing/*", MetaTestRunner::Expected::ToFail, 0, 2 ); + } - SECTION( "selftest/expected result/succeeding tests", - "Tests in the 'succeeding' branch succeed" ) { - MetaTestRunner::runMatching( "./succeeding/*", MetaTestRunner::Expected::ToSucceed, 1, 2 ); + SECTION( "selftest/expected result/succeeding tests", + "Tests in the 'succeeding' branch succeed" ) { + MetaTestRunner::runMatching( "./succeeding/*", MetaTestRunner::Expected::ToSucceed, 1, 2 ); + } + } + + /////////////////////////////////////////////////////////////////////////// + SECTION( "selftest/test counts", + "Number of test cases that run is fixed" ) { + EmbeddedRunner runner; + + SECTION( "selftest/test counts/succeeding tests", + "Number of 'succeeding' tests is fixed" ) { + Totals totals = runner.runMatching( "./succeeding/*", 0, 2 ); + CHECK( totals.assertions.passed == 298 ); + CHECK( totals.assertions.failed == 0 ); + } + + SECTION( "selftest/test counts/failing tests", + "Number of 'failing' tests is fixed" ) { + Totals totals = runner.runMatching( "./failing/*", 1, 2 ); + CHECK( totals.assertions.passed == 2 ); + CHECK( totals.assertions.failed == 77 ); + } } } - /////////////////////////////////////////////////////////////////////////// - SECTION( "selftest/test counts", - "Number of test cases that run is fixed" ) { - EmbeddedRunner runner; - - SECTION( "selftest/test counts/succeeding tests", - "Number of 'succeeding' tests is fixed" ) { - Totals totals = runner.runMatching( "./succeeding/*", 0, 2 ); - CHECK( totals.assertions.passed == 298 ); - CHECK( totals.assertions.failed == 0 ); - } - - SECTION( "selftest/test counts/failing tests", - "Number of 'failing' tests is fixed" ) { - Totals totals = runner.runMatching( "./failing/*", 1, 2 ); - CHECK( totals.assertions.passed == 2 ); - CHECK( totals.assertions.failed == 77 ); - } - } -} - -TEST_CASE( "meta/Misc/Sections", "looped tests" ) { - Catch::EmbeddedRunner runner; + TEST_CASE( "meta/Misc/Sections", "looped tests" ) { + Catch::EmbeddedRunner runner; - Catch::Totals totals = runner.runMatching( "./mixed/Misc/Sections/nested2", 0, 1 ); - CHECK( totals.assertions.passed == 2 ); - CHECK( totals.assertions.failed == 1 ); + Catch::Totals totals = runner.runMatching( "./mixed/Misc/Sections/nested2", 0, 1 ); + CHECK( totals.assertions.passed == 2 ); + CHECK( totals.assertions.failed == 1 ); + } } - #ifdef __clang__ #pragma clang diagnostic ignored "-Wweak-vtables" #endif @@ -68,528 +70,531 @@ TEST_CASE( "meta/Misc/Sections", "looped tests" ) { #include "../../include/internal/catch_test_spec.h" #include "../../include/reporters/catch_reporter_xml.hpp" -template -void parseIntoConfig( const char * (&argv)[size], Catch::ConfigData& config ) { - Clara::CommandLine parser = Catch::makeCommandLineParser(); - parser.parseInto( size, argv, config ); -} +namespace TestMain { -template -std::string parseIntoConfigAndReturnError( const char * (&argv)[size], Catch::ConfigData& config ) { - try { - parseIntoConfig( argv, config ); - FAIL( "expected exception" ); + template + void parseIntoConfig( const char * (&argv)[size], Catch::ConfigData& config ) { + Clara::CommandLine parser = Catch::makeCommandLineParser(); + parser.parseInto( size, argv, config ); } - catch( std::exception& ex ) { - return ex.what(); + + template + std::string parseIntoConfigAndReturnError( const char * (&argv)[size], Catch::ConfigData& config ) { + try { + parseIntoConfig( argv, config ); + FAIL( "expected exception" ); + } + catch( std::exception& ex ) { + return ex.what(); + } + return ""; } - return ""; -} -inline Catch::TestCase fakeTestCase( const char* name, const char* desc = "" ){ return Catch::makeTestCase( NULL, "", name, desc, CATCH_INTERNAL_LINEINFO ); } + inline Catch::TestCase fakeTestCase( const char* name, const char* desc = "" ){ return Catch::makeTestCase( NULL, "", name, desc, CATCH_INTERNAL_LINEINFO ); } -TEST_CASE( "Process can be configured on command line", "[config][command-line]" ) { + TEST_CASE( "Process can be configured on command line", "[config][command-line]" ) { - Catch::ConfigData config; + Catch::ConfigData config; - SECTION( "default - no arguments", "" ) { - const char* argv[] = { "test" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "default - no arguments", "" ) { + const char* argv[] = { "test" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - CHECK( config.shouldDebugBreak == false ); - CHECK( config.abortAfter == -1 ); - CHECK( config.noThrow == false ); - CHECK( config.reporterName.empty() ); - } + CHECK( config.shouldDebugBreak == false ); + CHECK( config.abortAfter == -1 ); + CHECK( config.noThrow == false ); + CHECK( config.reporterName.empty() ); + } - SECTION( "test lists", "" ) { - SECTION( "1 test", "Specify one test case using" ) { - const char* argv[] = { "test", "test1" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "test lists", "" ) { + SECTION( "1 test", "Specify one test case using" ) { + const char* argv[] = { "test", "test1" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - Catch::Config cfg( config ); - REQUIRE( cfg.filters().size() == 1 ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "notIncluded" ) ) == false ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) ); + Catch::Config cfg( config ); + REQUIRE( cfg.filters().size() == 1 ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "notIncluded" ) ) == false ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) ); + } + SECTION( "Specify one test case exclusion using exclude:", "" ) { + const char* argv[] = { "test", "exclude:test1" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + Catch::Config cfg( config ); + REQUIRE( cfg.filters().size() == 1 ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) == false ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "alwaysIncluded" ) ) ); + } + + SECTION( "Specify one test case exclusion using ~", "" ) { + const char* argv[] = { "test", "~test1" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + Catch::Config cfg( config ); + REQUIRE( cfg.filters().size() == 1 ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) == false ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "alwaysIncluded" ) ) ); + } + + SECTION( "Specify two test cases using -t", "" ) { + const char* argv[] = { "test", "-t", "test1", "test2" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + Catch::Config cfg( config ); + REQUIRE( cfg.filters().size() == 1 ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "notIncluded" ) ) == false ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) ); + REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test2" ) ) ); + } } - SECTION( "Specify one test case exclusion using exclude:", "" ) { - const char* argv[] = { "test", "exclude:test1" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - Catch::Config cfg( config ); - REQUIRE( cfg.filters().size() == 1 ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) == false ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "alwaysIncluded" ) ) ); + + SECTION( "reporter", "" ) { + SECTION( "-r/console", "" ) { + const char* argv[] = { "test", "-r", "console" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.reporterName == "console" ); + } + SECTION( "-r/xml", "" ) { + const char* argv[] = { "test", "-r", "xml" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.reporterName == "xml" ); + } + SECTION( "--reporter/junit", "" ) { + const char* argv[] = { "test", "--reporter", "junit" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.reporterName == "junit" ); + } } - - SECTION( "Specify one test case exclusion using ~", "" ) { - const char* argv[] = { "test", "~test1" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - Catch::Config cfg( config ); - REQUIRE( cfg.filters().size() == 1 ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) == false ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "alwaysIncluded" ) ) ); + + SECTION( "debugger", "" ) { + SECTION( "-b", "" ) { + const char* argv[] = { "test", "-b" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.shouldDebugBreak == true ); + } + SECTION( "--break", "" ) { + const char* argv[] = { "test", "--break" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.shouldDebugBreak ); + } } - SECTION( "Specify two test cases using -t", "" ) { - const char* argv[] = { "test", "-t", "test1", "test2" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "abort", "" ) { + SECTION( "-a aborts after first failure", "" ) { + const char* argv[] = { "test", "-a" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - Catch::Config cfg( config ); - REQUIRE( cfg.filters().size() == 1 ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "notIncluded" ) ) == false ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test1" ) ) ); - REQUIRE( cfg.filters()[0].shouldInclude( fakeTestCase( "test2" ) ) ); + REQUIRE( config.abortAfter == 1 ); + } + SECTION( "-x 2 aborts after two failures", "" ) { + const char* argv[] = { "test", "-x", "2" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.abortAfter == 2 ); + } + SECTION( "-x must be greater than zero", "" ) { + const char* argv[] = { "test", "-x", "0" }; + REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "greater than zero" ) ); + } + SECTION( "-x must be numeric", "" ) { + const char* argv[] = { "test", "-x", "oops" }; + REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "-x" ) ); + } } - } - SECTION( "reporter", "" ) { - SECTION( "-r/console", "" ) { - const char* argv[] = { "test", "-r", "console" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.reporterName == "console" ); + SECTION( "nothrow", "" ) { + SECTION( "-e", "" ) { + const char* argv[] = { "test", "-e" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.noThrow == true ); + } + SECTION( "--nothrow", "" ) { + const char* argv[] = { "test", "--nothrow" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.noThrow == true ); + } } - SECTION( "-r/xml", "" ) { - const char* argv[] = { "test", "-r", "xml" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.reporterName == "xml" ); - } - SECTION( "--reporter/junit", "" ) { - const char* argv[] = { "test", "--reporter", "junit" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.reporterName == "junit" ); + + SECTION( "output filename", "" ) { + SECTION( "-o filename", "" ) { + const char* argv[] = { "test", "-o", "filename.ext" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.outputFilename == "filename.ext" ); + } + SECTION( "--out", "" ) { + const char* argv[] = { "test", "--out", "filename.ext" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + REQUIRE( config.outputFilename == "filename.ext" ); + } } + + SECTION( "combinations", "" ) { + SECTION( "Single character flags can be combined", "" ) { + const char* argv[] = { "test", "-abe" }; + CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + + CHECK( config.abortAfter == 1 ); + CHECK( config.shouldDebugBreak ); + CHECK( config.noThrow == true ); + } + } } + + TEST_CASE( "selftest/test filter", "Individual filters" ) { + + Catch::TestCaseFilter matchAny( "*" ); + Catch::TestCaseFilter matchNone( "*", Catch::IfFilterMatches::ExcludeTests ); + CHECK( matchAny.shouldInclude( fakeTestCase( "any" ) )); + CHECK( matchNone.shouldInclude( fakeTestCase( "any" ) ) == false ); + + Catch::TestCaseFilter matchHidden( "./*" ); + Catch::TestCaseFilter matchNonHidden( "./*", Catch::IfFilterMatches::ExcludeTests ); + + CHECK( matchHidden.shouldInclude( fakeTestCase( "any" ) ) == false ); + CHECK( matchNonHidden.shouldInclude( fakeTestCase( "any" ) ) ); + + CHECK( matchHidden.shouldInclude( fakeTestCase( "./any" ) ) ); + CHECK( matchNonHidden.shouldInclude( fakeTestCase( "./any" ) ) == false ); + } + + TEST_CASE( "selftest/test filters", "Sets of filters" ) { + + Catch::TestCaseFilter matchHidden( "./*" ); + Catch::TestCaseFilter dontMatchA( "./a*", Catch::IfFilterMatches::ExcludeTests ); + Catch::TestCaseFilters filters( "" ); + filters.addFilter( matchHidden ); + filters.addFilter( dontMatchA ); + + CHECK( matchHidden.shouldInclude( fakeTestCase( "./something" ) ) ); + + CHECK( filters.shouldInclude( fakeTestCase( "any" ) ) == false ); + CHECK( filters.shouldInclude( fakeTestCase( "./something" ) ) ); + CHECK( filters.shouldInclude( fakeTestCase( "./anything" ) ) == false ); + } + + TEST_CASE( "selftest/filter/prefix wildcard", "Individual filters with wildcards at the start" ) { + Catch::TestCaseFilter matchBadgers( "*badger" ); + + CHECK( matchBadgers.shouldInclude( fakeTestCase( "big badger" ) )); + CHECK( matchBadgers.shouldInclude( fakeTestCase( "little badgers" ) ) == false ); + } + TEST_CASE( "selftest/filter/wildcard at both ends", "Individual filters with wildcards at both ends" ) { + Catch::TestCaseFilter matchBadgers( "*badger*" ); + + CHECK( matchBadgers.shouldInclude( fakeTestCase( "big badger" ) )); + CHECK( matchBadgers.shouldInclude( fakeTestCase( "little badgers" ) ) ); + CHECK( matchBadgers.shouldInclude( fakeTestCase( "badgers are big" ) ) ); + CHECK( matchBadgers.shouldInclude( fakeTestCase( "hedgehogs" ) ) == false ); + } + + + template + int getArgc( const char * (&)[size] ) { + return size; + } + + TEST_CASE( "selftest/tags", "[tags]" ) { + + std::string p1 = "[one]"; + std::string p2 = "[one],[two]"; + std::string p3 = "[one][two]"; + std::string p4 = "[one][two],[three]"; + std::string p5 = "[one][two]~[.],[three]"; - SECTION( "debugger", "" ) { - SECTION( "-b", "" ) { - const char* argv[] = { "test", "-b" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.shouldDebugBreak == true ); + SECTION( "single [one] tag", "" ) { + Catch::TestCase oneTag = makeTestCase( NULL, "", "test", "[one]", CATCH_INTERNAL_LINEINFO ); + + CHECK( oneTag.getTestCaseInfo().description == "" ); + CHECK( oneTag.hasTag( "one" ) ); + CHECK( oneTag.getTags().size() == 1 ); + + CHECK( oneTag.matchesTags( p1 ) == true ); + CHECK( oneTag.matchesTags( p2 ) == true ); + CHECK( oneTag.matchesTags( p3 ) == false ); + CHECK( oneTag.matchesTags( p4 ) == false ); + CHECK( oneTag.matchesTags( p5 ) == false ); } - SECTION( "--break", "" ) { - const char* argv[] = { "test", "--break" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.shouldDebugBreak ); + + SECTION( "single [two] tag", "" ) { + Catch::TestCase oneTag = makeTestCase( NULL, "", "test", "[two]", CATCH_INTERNAL_LINEINFO ); + + CHECK( oneTag.getTestCaseInfo().description == "" ); + CHECK( oneTag.hasTag( "two" ) ); + CHECK( oneTag.getTags().size() == 1 ); + + CHECK( oneTag.matchesTags( p1 ) == false ); + CHECK( oneTag.matchesTags( p2 ) == true ); + CHECK( oneTag.matchesTags( p3 ) == false ); + CHECK( oneTag.matchesTags( p4 ) == false ); + CHECK( oneTag.matchesTags( p5 ) == false ); + } + + SECTION( "two tags", "" ) { + Catch::TestCase twoTags= makeTestCase( NULL, "", "test", "[one][two]", CATCH_INTERNAL_LINEINFO ); + + CHECK( twoTags.getTestCaseInfo().description == "" ); + CHECK( twoTags.hasTag( "one" ) ); + CHECK( twoTags.hasTag( "two" ) ); + CHECK( twoTags.hasTag( "Two" ) ); + CHECK( twoTags.hasTag( "three" ) == false ); + CHECK( twoTags.getTags().size() == 2 ); + + CHECK( twoTags.matchesTags( p1 ) == true ); + CHECK( twoTags.matchesTags( p2 ) == true ); + CHECK( twoTags.matchesTags( p3 ) == true ); + CHECK( twoTags.matchesTags( p4 ) == true ); + CHECK( twoTags.matchesTags( p5 ) == true ); + } + SECTION( "complex", "" ) { + CHECK( fakeTestCase( "test", "[one][.]" ).matchesTags( p1 ) ); + CHECK_FALSE( fakeTestCase( "test", "[one][.]" ).matchesTags( p5 ) ); + CHECK( fakeTestCase( "test", "[three]" ).matchesTags( p4 ) ); + CHECK( fakeTestCase( "test", "[three]" ).matchesTags( p5 ) ); + CHECK( fakeTestCase( "test", "[three]" ).matchesTags( "[three]~[one]" ) ); + CHECK( fakeTestCase( "test", "[unit][not_apple]" ).matchesTags( "[unit]" ) ); + CHECK_FALSE( fakeTestCase( "test", "[unit][not_apple]" ).matchesTags( "[unit]~[not_apple]" ) ); + } + + SECTION( "one tag with characters either side", "" ) { + + Catch::TestCase oneTagWithExtras = makeTestCase( NULL, "", "test", "12[one]34", CATCH_INTERNAL_LINEINFO ); + CHECK( oneTagWithExtras.getTestCaseInfo().description == "1234" ); + CHECK( oneTagWithExtras.hasTag( "one" ) ); + CHECK( oneTagWithExtras.hasTag( "two" ) == false ); + CHECK( oneTagWithExtras.getTags().size() == 1 ); + } + + SECTION( "start of a tag, but not closed", "" ) { + + Catch::TestCase oneTagOpen = makeTestCase( NULL, "", "test", "[one", CATCH_INTERNAL_LINEINFO ); + + CHECK( oneTagOpen.getTestCaseInfo().description == "[one" ); + CHECK( oneTagOpen.hasTag( "one" ) == false ); + CHECK( oneTagOpen.getTags().size() == 0 ); + } + + SECTION( "hidden", "" ) { + Catch::TestCase oneTag = makeTestCase( NULL, "", "test", "[.]", CATCH_INTERNAL_LINEINFO ); + + CHECK( oneTag.getTestCaseInfo().description == "" ); + CHECK( oneTag.hasTag( "." ) ); + CHECK( oneTag.isHidden() ); + + CHECK( oneTag.matchesTags( "~[.]" ) == false ); + } } + + TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { + + using namespace Catch; + SECTION( "plain string", "" ) { + // guide: 123456789012345678 + std::string testString = "one two three four"; - SECTION( "abort", "" ) { - SECTION( "-a aborts after first failure", "" ) { - const char* argv[] = { "test", "-a" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); + SECTION( "No wrapping", "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ); + } + SECTION( "Wrapped once", "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 17 ) ).toString() == "one two three\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 16 ) ).toString() == "one two three\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 14 ) ).toString() == "one two three\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 13 ) ).toString() == "one two three\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 12 ) ).toString() == "one two\nthree four" ); + } + SECTION( "Wrapped twice", "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ); + } + SECTION( "Wrapped three times", "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one\ntwo\nthree\nfour" ); + } + SECTION( "Short wrap", "" ) { + CHECK( Catch::Text( "abcdef", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef" ); + CHECK( Catch::Text( "abcdefg", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndefg" ); + CHECK( Catch::Text( "abcdefgh", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef-\ngh" ); - REQUIRE( config.abortAfter == 1 ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one\ntwo\nthr-\nee\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 3 ) ).toString() == "one\ntwo\nth-\nree\nfo-\nur" ); + } + SECTION( "As container", "" ) { + Catch::Text text( testString, TextAttributes().setWidth( 6 ) ); + REQUIRE( text.size() == 4 ); + CHECK( text[0] == "one" ); + CHECK( text[1] == "two" ); + CHECK( text[2] == "three" ); + CHECK( text[3] == "four" ); + } + SECTION( "Indent first line differently", "" ) { + Catch::Text text( testString, TextAttributes() + .setWidth( 10 ) + .setIndent( 4 ) + .setInitialIndent( 1 ) ); + CHECK( text.toString() == " one two\n three\n four" ); + } + } - SECTION( "-x 2 aborts after two failures", "" ) { - const char* argv[] = { "test", "-x", "2" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.abortAfter == 2 ); - } - SECTION( "-x must be greater than zero", "" ) { - const char* argv[] = { "test", "-x", "0" }; - REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "greater than zero" ) ); - } - SECTION( "-x must be numeric", "" ) { - const char* argv[] = { "test", "-x", "oops" }; - REQUIRE_THAT( parseIntoConfigAndReturnError( argv, config ), Contains( "-x" ) ); - } - } - SECTION( "nothrow", "" ) { - SECTION( "-e", "" ) { - const char* argv[] = { "test", "-e" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.noThrow == true ); + SECTION( "With newlines", "" ) { + + // guide: 1234567890123456789 + std::string testString = "one two\nthree four"; + + SECTION( "No wrapping" , "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 10 ) ).toString() == testString ); + } + SECTION( "Trailing newline" , "" ) { + CHECK( Catch::Text( "abcdef\n", TextAttributes().setWidth( 10 ) ).toString() == "abcdef\n" ); + CHECK( Catch::Text( "abcdef", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" ); + CHECK( Catch::Text( "abcdef\n", TextAttributes().setWidth( 6 ) ).toString() == "abcdef\n" ); + } + SECTION( "Wrapped once", "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ); + } + SECTION( "Wrapped twice", "" ) { + CHECK( Catch::Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); + } } - SECTION( "--nothrow", "" ) { - const char* argv[] = { "test", "--nothrow" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.noThrow == true ); - } - } - - SECTION( "output filename", "" ) { - SECTION( "-o filename", "" ) { - const char* argv[] = { "test", "-o", "filename.ext" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.outputFilename == "filename.ext" ); - } - SECTION( "--out", "" ) { - const char* argv[] = { "test", "--out", "filename.ext" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - REQUIRE( config.outputFilename == "filename.ext" ); - } - } - - SECTION( "combinations", "" ) { - SECTION( "Single character flags can be combined", "" ) { - const char* argv[] = { "test", "-abe" }; - CHECK_NOTHROW( parseIntoConfig( argv, config ) ); - - CHECK( config.abortAfter == 1 ); - CHECK( config.shouldDebugBreak ); - CHECK( config.noThrow == true ); - } - } -} - -TEST_CASE( "selftest/test filter", "Individual filters" ) { - - Catch::TestCaseFilter matchAny( "*" ); - Catch::TestCaseFilter matchNone( "*", Catch::IfFilterMatches::ExcludeTests ); - CHECK( matchAny.shouldInclude( fakeTestCase( "any" ) )); - CHECK( matchNone.shouldInclude( fakeTestCase( "any" ) ) == false ); - - Catch::TestCaseFilter matchHidden( "./*" ); - Catch::TestCaseFilter matchNonHidden( "./*", Catch::IfFilterMatches::ExcludeTests ); - - CHECK( matchHidden.shouldInclude( fakeTestCase( "any" ) ) == false ); - CHECK( matchNonHidden.shouldInclude( fakeTestCase( "any" ) ) ); - - CHECK( matchHidden.shouldInclude( fakeTestCase( "./any" ) ) ); - CHECK( matchNonHidden.shouldInclude( fakeTestCase( "./any" ) ) == false ); -} - -TEST_CASE( "selftest/test filters", "Sets of filters" ) { - - Catch::TestCaseFilter matchHidden( "./*" ); - Catch::TestCaseFilter dontMatchA( "./a*", Catch::IfFilterMatches::ExcludeTests ); - Catch::TestCaseFilters filters( "" ); - filters.addFilter( matchHidden ); - filters.addFilter( dontMatchA ); - - CHECK( matchHidden.shouldInclude( fakeTestCase( "./something" ) ) ); - - CHECK( filters.shouldInclude( fakeTestCase( "any" ) ) == false ); - CHECK( filters.shouldInclude( fakeTestCase( "./something" ) ) ); - CHECK( filters.shouldInclude( fakeTestCase( "./anything" ) ) == false ); -} - -TEST_CASE( "selftest/filter/prefix wildcard", "Individual filters with wildcards at the start" ) { - Catch::TestCaseFilter matchBadgers( "*badger" ); - - CHECK( matchBadgers.shouldInclude( fakeTestCase( "big badger" ) )); - CHECK( matchBadgers.shouldInclude( fakeTestCase( "little badgers" ) ) == false ); -} -TEST_CASE( "selftest/filter/wildcard at both ends", "Individual filters with wildcards at both ends" ) { - Catch::TestCaseFilter matchBadgers( "*badger*" ); - - CHECK( matchBadgers.shouldInclude( fakeTestCase( "big badger" ) )); - CHECK( matchBadgers.shouldInclude( fakeTestCase( "little badgers" ) ) ); - CHECK( matchBadgers.shouldInclude( fakeTestCase( "badgers are big" ) ) ); - CHECK( matchBadgers.shouldInclude( fakeTestCase( "hedgehogs" ) ) == false ); -} - - -template -int getArgc( const char * (&)[size] ) { - return size; -} - -TEST_CASE( "selftest/tags", "[tags]" ) { - - std::string p1 = "[one]"; - std::string p2 = "[one],[two]"; - std::string p3 = "[one][two]"; - std::string p4 = "[one][two],[three]"; - std::string p5 = "[one][two]~[.],[three]"; - SECTION( "single [one] tag", "" ) { - Catch::TestCase oneTag = makeTestCase( NULL, "", "test", "[one]", CATCH_INTERNAL_LINEINFO ); + SECTION( "With tabs", "" ) { - CHECK( oneTag.getTestCaseInfo().description == "" ); - CHECK( oneTag.hasTag( "one" ) ); - CHECK( oneTag.getTags().size() == 1 ); - - CHECK( oneTag.matchesTags( p1 ) == true ); - CHECK( oneTag.matchesTags( p2 ) == true ); - CHECK( oneTag.matchesTags( p3 ) == false ); - CHECK( oneTag.matchesTags( p4 ) == false ); - CHECK( oneTag.matchesTags( p5 ) == false ); - } - - SECTION( "single [two] tag", "" ) { - Catch::TestCase oneTag = makeTestCase( NULL, "", "test", "[two]", CATCH_INTERNAL_LINEINFO ); - - CHECK( oneTag.getTestCaseInfo().description == "" ); - CHECK( oneTag.hasTag( "two" ) ); - CHECK( oneTag.getTags().size() == 1 ); - - CHECK( oneTag.matchesTags( p1 ) == false ); - CHECK( oneTag.matchesTags( p2 ) == true ); - CHECK( oneTag.matchesTags( p3 ) == false ); - CHECK( oneTag.matchesTags( p4 ) == false ); - CHECK( oneTag.matchesTags( p5 ) == false ); - } - - SECTION( "two tags", "" ) { - Catch::TestCase twoTags= makeTestCase( NULL, "", "test", "[one][two]", CATCH_INTERNAL_LINEINFO ); - - CHECK( twoTags.getTestCaseInfo().description == "" ); - CHECK( twoTags.hasTag( "one" ) ); - CHECK( twoTags.hasTag( "two" ) ); - CHECK( twoTags.hasTag( "Two" ) ); - CHECK( twoTags.hasTag( "three" ) == false ); - CHECK( twoTags.getTags().size() == 2 ); - - CHECK( twoTags.matchesTags( p1 ) == true ); - CHECK( twoTags.matchesTags( p2 ) == true ); - CHECK( twoTags.matchesTags( p3 ) == true ); - CHECK( twoTags.matchesTags( p4 ) == true ); - CHECK( twoTags.matchesTags( p5 ) == true ); - } - SECTION( "complex", "" ) { - CHECK( fakeTestCase( "test", "[one][.]" ).matchesTags( p1 ) ); - CHECK_FALSE( fakeTestCase( "test", "[one][.]" ).matchesTags( p5 ) ); - CHECK( fakeTestCase( "test", "[three]" ).matchesTags( p4 ) ); - CHECK( fakeTestCase( "test", "[three]" ).matchesTags( p5 ) ); - CHECK( fakeTestCase( "test", "[three]" ).matchesTags( "[three]~[one]" ) ); - CHECK( fakeTestCase( "test", "[unit][not_apple]" ).matchesTags( "[unit]" ) ); - CHECK_FALSE( fakeTestCase( "test", "[unit][not_apple]" ).matchesTags( "[unit]~[not_apple]" ) ); - } - - SECTION( "one tag with characters either side", "" ) { - - Catch::TestCase oneTagWithExtras = makeTestCase( NULL, "", "test", "12[one]34", CATCH_INTERNAL_LINEINFO ); - CHECK( oneTagWithExtras.getTestCaseInfo().description == "1234" ); - CHECK( oneTagWithExtras.hasTag( "one" ) ); - CHECK( oneTagWithExtras.hasTag( "two" ) == false ); - CHECK( oneTagWithExtras.getTags().size() == 1 ); - } + // guide: 1234567890123456789 + std::string testString = "one two \tthree four five six"; + + CHECK( Catch::Text( testString, TextAttributes().setWidth( 15 ) ).toString() + == "one two three\n four\n five\n six" ); + } + - SECTION( "start of a tag, but not closed", "" ) { - - Catch::TestCase oneTagOpen = makeTestCase( NULL, "", "test", "[one", CATCH_INTERNAL_LINEINFO ); - - CHECK( oneTagOpen.getTestCaseInfo().description == "[one" ); - CHECK( oneTagOpen.hasTag( "one" ) == false ); - CHECK( oneTagOpen.getTags().size() == 0 ); } - SECTION( "hidden", "" ) { - Catch::TestCase oneTag = makeTestCase( NULL, "", "test", "[.]", CATCH_INTERNAL_LINEINFO ); - - CHECK( oneTag.getTestCaseInfo().description == "" ); - CHECK( oneTag.hasTag( "." ) ); - CHECK( oneTag.isHidden() ); - - CHECK( oneTag.matchesTags( "~[.]" ) == false ); - - } -} - -TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { - using namespace Catch; - SECTION( "plain string", "" ) { - // guide: 123456789012345678 - std::string testString = "one two three four"; - - SECTION( "No wrapping", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ); - } - SECTION( "Wrapped once", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 17 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 16 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 14 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 13 ) ).toString() == "one two three\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 12 ) ).toString() == "one two\nthree four" ); - } - SECTION( "Wrapped twice", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ); - } - SECTION( "Wrapped three times", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one\ntwo\nthree\nfour" ); - } - SECTION( "Short wrap", "" ) { - CHECK( Text( "abcdef", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef" ); - CHECK( Text( "abcdefg", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndefg" ); - CHECK( Text( "abcdefgh", TextAttributes().setWidth( 4 ) ).toString() == "abc-\ndef-\ngh" ); - CHECK( Text( testString, TextAttributes().setWidth( 4 ) ).toString() == "one\ntwo\nthr-\nee\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 3 ) ).toString() == "one\ntwo\nth-\nree\nfo-\nur" ); - } - SECTION( "As container", "" ) { - Text text( testString, TextAttributes().setWidth( 6 ) ); - REQUIRE( text.size() == 4 ); - CHECK( text[0] == "one" ); - CHECK( text[1] == "two" ); - CHECK( text[2] == "three" ); - CHECK( text[3] == "four" ); - } - SECTION( "Indent first line differently", "" ) { - Text text( testString, TextAttributes() - .setWidth( 10 ) - .setIndent( 4 ) - .setInitialIndent( 1 ) ); - CHECK( text.toString() == " one two\n three\n four" ); - } - - } - - SECTION( "With newlines", "" ) { - - // guide: 1234567890123456789 - std::string testString = "one two\nthree four"; - - SECTION( "No wrapping" , "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 18 ) ).toString() == testString ); - CHECK( Text( testString, TextAttributes().setWidth( 10 ) ).toString() == testString ); - } - SECTION( "Trailing newline" , "" ) { - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 10 ) ).toString() == "abcdef\n" ); - CHECK( Text( "abcdef", TextAttributes().setWidth( 6 ) ).toString() == "abcdef" ); - CHECK( Text( "abcdef\n", TextAttributes().setWidth( 6 ) ).toString() == "abcdef\n" ); - } - SECTION( "Wrapped once", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 9 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 8 ) ).toString() == "one two\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 7 ) ).toString() == "one two\nthree\nfour" ); - } - SECTION( "Wrapped twice", "" ) { - CHECK( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); - } - } - - SECTION( "With tabs", "" ) { + class ColourString { + public: - // guide: 1234567890123456789 - std::string testString = "one two \tthree four five six"; - - CHECK( Text( testString, TextAttributes().setWidth( 15 ) ).toString() - == "one two three\n four\n five\n six" ); - } - - -} + struct ColourIndex { + ColourIndex( Colour::Code _colour, std::size_t _fromIndex, std::size_t _toIndex ) + : colour( _colour ), + fromIndex( _fromIndex ), + toIndex( _toIndex ) + {} -using namespace Catch; + Colour::Code colour; + std::size_t fromIndex; + std::size_t toIndex; + }; -class ColourString { -public: - - struct ColourIndex { - ColourIndex( Colour::Code _colour, std::size_t _fromIndex, std::size_t _toIndex ) - : colour( _colour ), - fromIndex( _fromIndex ), - toIndex( _toIndex ) + ColourString( std::string const& _string ) + : string( _string ) {} + ColourString( std::string const& _string, std::vector const& _colours ) + : string( _string ), colours( _colours ) + {} + + ColourString& addColour( Colour::Code colour, int _index ) { + colours.push_back( ColourIndex( colour, + resolveRelativeIndex( _index ), + resolveRelativeIndex( _index )+1 ) ); + return *this; + } + ColourString& addColour( Colour::Code colour, int _fromIndex, int _toIndex ) { + colours.push_back( ColourIndex( colour, + resolveRelativeIndex(_fromIndex), + resolveLastRelativeIndex( _toIndex ) ) ); + return *this; + } + + void writeToStream( std::ostream& _stream ) const { + std::size_t last = 0; + for( std::size_t i = 0; i < colours.size(); ++i ) { + ColourIndex const& index = colours[i]; + if( index.fromIndex > last ) + _stream << string.substr( last, index.fromIndex-last ); + { + Colour colourGuard( index.colour ); + _stream << string.substr( index.fromIndex, index.toIndex-index.fromIndex ); + } + last = index.toIndex; + } + if( last < string.size() ) + _stream << string.substr( last ); + } + friend std::ostream& operator << ( std::ostream& _stream, ColourString const& _colourString ) { + _colourString.writeToStream( _stream ); + return _stream; + } - Colour::Code colour; - std::size_t fromIndex; - std::size_t toIndex; + private: + + std::size_t resolveLastRelativeIndex( int _index ) { + std::size_t index = resolveRelativeIndex( _index ); + return index == 0 ? string.size() : index; + } + std::size_t resolveRelativeIndex( int _index ) { + return static_cast( _index >= 0 + ? _index + : static_cast( string.size() )+_index ); + } + std::string string; + std::vector colours; }; - ColourString( std::string const& _string ) - : string( _string ) - {} - ColourString( std::string const& _string, std::vector const& _colours ) - : string( _string ), colours( _colours ) - {} + // !TBD: This will be folded into Text class + TEST_CASE( "Strings can be rendered with colour", "[colour]" ) { - ColourString& addColour( Colour::Code colour, int _index ) { - colours.push_back( ColourIndex( colour, - resolveRelativeIndex( _index ), - resolveRelativeIndex( _index )+1 ) ); - return *this; - } - ColourString& addColour( Colour::Code colour, int _fromIndex, int _toIndex ) { - colours.push_back( ColourIndex( colour, - resolveRelativeIndex(_fromIndex), - resolveLastRelativeIndex( _toIndex ) ) ); - return *this; - } - - void writeToStream( std::ostream& _stream ) const { - std::size_t last = 0; - for( std::size_t i = 0; i < colours.size(); ++i ) { - ColourIndex const& index = colours[i]; - if( index.fromIndex > last ) - _stream << string.substr( last, index.fromIndex-last ); - { - Colour colourGuard( index.colour ); - _stream << string.substr( index.fromIndex, index.toIndex-index.fromIndex ); - } - last = index.toIndex; + { + ColourString cs( "hello" ); + cs .addColour( Colour::Red, 0 ) + .addColour( Colour::Green, -1 ); + + std::cout << cs << std::endl; } - if( last < string.size() ) - _stream << string.substr( last ); - } - friend std::ostream& operator << ( std::ostream& _stream, ColourString const& _colourString ) { - _colourString.writeToStream( _stream ); - return _stream; - } -private: - - std::size_t resolveLastRelativeIndex( int _index ) { - std::size_t index = resolveRelativeIndex( _index ); - return index == 0 ? string.size() : index; - } - std::size_t resolveRelativeIndex( int _index ) { - return static_cast( _index >= 0 - ? _index - : static_cast( string.size() )+_index ); - } - std::string string; - std::vector colours; -}; - -// !TBD: This will be folded into Text class -TEST_CASE( "Strings can be rendered with colour", "[colour]" ) { - - { - ColourString cs( "hello" ); - cs .addColour( Colour::Red, 0 ) - .addColour( Colour::Green, -1 ); - - std::cout << cs << std::endl; - } - - { - ColourString cs( "hello" ); - cs .addColour( Colour::Blue, 1, -2 ); + { + ColourString cs( "hello" ); + cs .addColour( Colour::Blue, 1, -2 ); - std::cout << cs << std::endl; + std::cout << cs << std::endl; + } + } + + TEST_CASE( "Text can be formatted using the Text class", "" ) { -} - -TEST_CASE( "Text can be formatted using the Text class", "" ) { - - CHECK( Text( "hi there" ).toString() == "hi there" ); - - TextAttributes narrow; - narrow.setWidth( 6 ); - - CHECK( Text( "hi there", narrow ).toString() == "hi\nthere" ); -} - -TEST_CASE( "Long text is truncted", "[Text][Truncated]" ) { - - std::string longLine( 90, '*' ); - - std::ostringstream oss; - for(int i = 0; i < 600; ++i ) - oss << longLine << longLine << "\n"; - Text t( oss.str() ); - CHECK_THAT( t.toString(), EndsWith( "... message truncated due to excessive size" ) ); - + CHECK( Catch::Text( "hi there" ).toString() == "hi there" ); + + TextAttributes narrow; + narrow.setWidth( 6 ); + + CHECK( Catch::Text( "hi there", narrow ).toString() == "hi\nthere" ); + } + + TEST_CASE( "Long text is truncted", "[Text][Truncated]" ) { + + std::string longLine( 90, '*' ); + + std::ostringstream oss; + for(int i = 0; i < 600; ++i ) + oss << longLine << longLine << "\n"; + Catch::Text t( oss.str() ); + CHECK_THAT( t.toString(), EndsWith( "... message truncated due to excessive size" ) ); + + } } diff --git a/projects/SelfTest/VisualStudioTests.cpp b/projects/SelfTest/VisualStudioTests.cpp new file mode 100644 index 00000000..b972a4ea --- /dev/null +++ b/projects/SelfTest/VisualStudioTests.cpp @@ -0,0 +1,110 @@ +/* + * Created by Phil on 22/10/2010. + * Copyright 2010 Two Blue Cubes Ltd + * + * 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) + */ + +#include "catch.hpp" + +#if defined(INTERNAL_CATCH_VS_MANAGED) || defined(INTERNAL_CATCH_VS_NATIVE) + +namespace VisualStudioTests +{ + class UniqueTestsFixture { + private: + static int uniqueID; + public: + UniqueTestsFixture() { } + protected: + int getID() { + return ++uniqueID; + } + }; + + int UniqueTestsFixture::uniqueID = 0; + TEST_CASE("M00", "[m_off]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(!show); + } + + CATCH_CONFIG_SHOW_SUCCESS(true) + TEST_CASE("M01", "[m_on]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(show); + } + + TEST_CASE("M02", "[m_off]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(!show); + } + + TEST_CASE_METHOD(UniqueTestsFixture, "M10", "[m_off]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(!show); + getID(); + } + + CATCH_CONFIG_WARN_MISSING_ASSERTIONS(true) + CATCH_CONFIG_SHOW_SUCCESS(true) + TEST_CASE_METHOD(UniqueTestsFixture, "M11", "[m_on]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(show); + getID(); + } + + CATCH_CONFIG_WARN_MISSING_ASSERTIONS(true) + CATCH_CONFIG_SHOW_SUCCESS(true) + TEST_CASE_METHOD(UniqueTestsFixture, "M99", "[m_on]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(show); + WARN("Warning message"); + getID(); + } + + TEST_CASE_METHOD(UniqueTestsFixture, "M12", "[m_off]") + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(!show); + getID(); + } + + class ConfigTest + { + public: + void run1() + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(!show); + } + void run2() + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(show); + } + void run3() + { + bool show = Catch::getCurrentContext().getConfig()->includeSuccessfulResults(); + REQUIRE(!show); + } + }; + METHOD_AS_TEST_CASE(ConfigTest::run1,"M20", "[m_off]"); + + CATCH_CONFIG_SHOW_SUCCESS(true) + METHOD_AS_TEST_CASE(ConfigTest::run2,"M21", "[m_on]"); + + METHOD_AS_TEST_CASE(ConfigTest::run3,"M22", "[m_off]"); + + CATCH_MAP_CATEGORY_TO_TAG(vstestsCheckOutputOff, "[m_off]"); + CATCH_CONFIG_SHOW_SUCCESS(true) + CATCH_MAP_CATEGORY_TO_TAG(vstestsCheckOutputOn, "[m_on]"); + CATCH_MAP_CATEGORY_TO_TAG(vstestsCheckOutputOff2, "[m_off]"); +} +#endif diff --git a/projects/SelfTest/catch_self_test.cpp b/projects/SelfTest/catch_self_test.cpp index c5153d68..4dce6fda 100644 --- a/projects/SelfTest/catch_self_test.cpp +++ b/projects/SelfTest/catch_self_test.cpp @@ -6,7 +6,9 @@ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ +#if !defined(_WINDLL) #define CATCH_CONFIG_MAIN +#endif #include "catch_self_test.hpp" namespace Catch{ diff --git a/projects/SelfTest/makefile b/projects/SelfTest/makefile index 9be05008..61ed50ec 100644 --- a/projects/SelfTest/makefile +++ b/projects/SelfTest/makefile @@ -4,8 +4,6 @@ SOURCES = ApproxTests.cpp \ ExceptionTests.cpp \ GeneratorTests.cpp \ MessageTests.cpp \ - MessageInstantiationTests1.cpp \ - MessageInstantiationTests2.cpp \ MiscTests.cpp \ TestMain.cpp \ TrickyTests.cpp \