diff --git a/.gitignore b/.gitignore index 45fc0fa8..9ed4427e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,12 @@ Debug Release ipch TestResults +projects/VS2010/TestCatch/Visual Lint +projects/VS2010/ManagedTestCatch/Visual Lint +projects/VS2012/ManagedTestCatch/Visual Lint +projects/VS2012/NativeTestCatch/Visual Lint +DebugMbcs +ReleaseMbcs *.user *.xcuserstate *.o diff --git a/docs/vs/VScommandlinetags.md b/docs/vs/VScommandlinetags.md new file mode 100644 index 00000000..6a38134d --- /dev/null +++ b/docs/vs/VScommandlinetags.md @@ -0,0 +1,8 @@ +If you've used the Catch command line, you might know that Catch can selectively include and exclude tests using tags. For example, you might want to exclude long running tests from each check-in to your CI server, so you might tag those tests with `[slow]` and run Catch with `example.exe ~[slow]`. + +It doesn't look like it's possible to reproduce the flexibility of Catch but it is possible to do some basic filtering using Catch tags. + + +--- + +[Home](../../README.md) \ No newline at end of file diff --git a/docs/vs/vs-index.md b/docs/vs/vs-index.md index a117b0d3..7b47e7a7 100644 --- a/docs/vs/vs-index.md +++ b/docs/vs/vs-index.md @@ -20,7 +20,15 @@ We can do the same for VS2012 - [see this page for instructions on how to do thi # Running tests from the command line -Tests can also be run from the command line. [For VS2010 see these details](VS2010commandline.md) and [for VS2012 see these](VS2012commandline.md) +Tests can also be run from the command line. [For VS2010 see these details](VS2010commandline.md) and [for VS2012 see these](VS2012commandline.md). + +# Running tests using Catch tags + +If you've used the Catch command line, you'll know that Catch can selectively include and exclude tests using tags. For example, you might want to exclude long running tests from each check-in to your CI server, so you might tag those tests with `[slow]` and run Catch with `example.exe ~[slow]`. + +It doesn't look like it's possible to reproduce the flexibility of Catch but it is possible to do some basic filtering using Catch tags. + +[This page details how to do this.](VScommandlinetags.md) # Differences in behaviour @@ -34,7 +42,7 @@ There are some minor differences in behaviour between Catch and Visual Studio pr You can still use the same names that you would normally use for Catch TEST_CASE names, however we use an internal name for the actual function name that the test lives in. This means that you won't see the Catch names in the Test View (VS2010) unless you 'Group By' Description, or in VS2012 you select the 'Traits' view Test Explorer - see the screen shots above. -## Catch tests always stop on first failure. +## When running from the GUI, Catch tests always stop on first failure. A Catch test that uses SECTIONS will continue to run further sections if one fails; in VS this doesn't make much sense because in a visual environment we want to capture the context of where the test failed. If we allowed the test to continue then we lose this context, so instead we always stop on each failure. You can still use the CHECK macros if you don't want the test to stop. diff --git a/include/catch.hpp b/include/catch.hpp index 80cc5c2a..4c675fe3 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 fd1cf11d..e0bc098d 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -58,11 +58,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 @@ -72,7 +68,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 @@ -86,10 +81,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) @@ -108,8 +102,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 @@ -121,7 +113,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 ) CATCH_BREAK_INTO_DEBUGGER(); \ 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 ); \ } @@ -135,7 +127,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(), \ @@ -174,7 +166,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 ) { \ @@ -218,7 +210,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 30cd34b9..110304e3 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 { @@ -41,6 +173,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 b20762f3..a7e4962b 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(STDOUT_FILENO); } - 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 de779e07..65472ddb 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 6438f205..2d9c59d1 100644 --- a/include/internal/catch_interfaces_runner.h +++ b/include/internal/catch_interfaces_runner.h @@ -14,6 +14,10 @@ namespace Catch { struct IRunner { virtual ~IRunner(); }; + + struct NullRunner : public IRunner + { + }; } #endif // TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED diff --git a/include/internal/catch_registry_hub.hpp b/include/internal/catch_registry_hub.hpp index 303a843a..a0d1df1e 100644 --- a/include/internal/catch_registry_hub.hpp +++ b/include/internal/catch_registry_hub.hpp @@ -16,64 +16,68 @@ namespace Catch { - namespace { + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { + return m_exceptionTranslatorRegistry; + } - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { - return m_exceptionTranslatorRegistry; - } + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerTest( TestCase const& testInfo ) { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerTest( TestCase const& testInfo ) { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - }; - - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = NULL; + // Single, global, instance + template + struct GlobalRegistryHub + { + static T*& instance() + { if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); + theRegistryHub = new T(); return theRegistryHub; } - } + static T* theRegistryHub; + }; + template + T* GlobalRegistryHub::theRegistryHub = NULL; - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); + INTERNAL_CATCH_INLINE IRegistryHub& getRegistryHub() { + return *GlobalRegistryHub::instance(); } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); + INTERNAL_CATCH_INLINE IMutableRegistryHub& getMutableRegistryHub() { + return *GlobalRegistryHub::instance(); } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = NULL; + INTERNAL_CATCH_INLINE void cleanUp() { + delete GlobalRegistryHub::instance(); + GlobalRegistryHub::instance() = NULL; cleanUpContext(); } - std::string translateActiveException() { + INTERNAL_CATCH_INLINE std::string translateActiveException() { return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); } diff --git a/include/internal/catch_runner_impl.hpp b/include/internal/catch_runner_impl.hpp index bd392f94..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,37 +265,16 @@ namespace Catch { else { m_activeTestCase->invoke(); } - duration = timer.getElapsedSeconds(); + finaliser.stopTimer(); } -#ifdef INTERNAL_CATCH_VS_MANAGED // detect CLR - catch(AssertFailedException^) { - 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: @@ -319,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_test_case_registry_impl.hpp b/include/internal/catch_test_case_registry_impl.hpp index af1ee29f..3c8aa507 100644 --- a/include/internal/catch_test_case_registry_impl.hpp +++ b/include/internal/catch_test_case_registry_impl.hpp @@ -8,7 +8,11 @@ #ifndef TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED +#if defined(INTERNAL_CATCH_VS_MANAGED) || defined(INTERNAL_CATCH_VS_NATIVE) +#include "internal/catch_vs_test_registry.hpp" +#else #include "catch_test_registry.hpp" +#endif #include "catch_test_case_info.h" #include "catch_test_spec.h" #include "catch_context.h" @@ -124,15 +128,15 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - AutoReg::AutoReg( TestFunction function, + INTERNAL_CATCH_INLINE AutoReg::AutoReg( TestFunction function, SourceLineInfo const& lineInfo, NameAndDesc const& nameAndDesc ) { registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); } - AutoReg::~AutoReg() {} + INTERNAL_CATCH_INLINE AutoReg::~AutoReg() {} - void AutoReg::registerTestCase( ITestCase* testCase, + INTERNAL_CATCH_INLINE void AutoReg::registerTestCase( ITestCase* testCase, char const* classOrQualifiedMethodName, NameAndDesc const& nameAndDesc, SourceLineInfo const& lineInfo ) { 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 b06d5e8b..85e83c75 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; @@ -33,67 +33,50 @@ namespace Catch { #include "catch_section.hpp" #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 "internal/catch_exception_translator_registry.hpp" +#include "catch_registry_hub.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() {} - 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 #endif // TWOBLUECUBES_CATCH_VS_MANAGED_HPP_INCLUDED diff --git a/include/internal/catch_vs_native_impl.hpp b/include/internal/catch_vs_native_impl.hpp index da4a714d..71b8472c 100644 --- a/include/internal/catch_vs_native_impl.hpp +++ b/include/internal/catch_vs_native_impl.hpp @@ -16,8 +16,6 @@ using Microsoft::VisualStudio::CppUnitTestFramework::Logger; using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using Microsoft::VisualStudio::CppUnitTestFramework::__LineInfo; -#define INTERNAL_CATCH_INLINE inline - #include #include @@ -26,66 +24,50 @@ using Microsoft::VisualStudio::CppUnitTestFramework::__LineInfo; #include "catch_tags.hpp" #include "catch_test_spec.hpp" #include "catch_section.hpp" -#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() {} - 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 fc6d2897..6071ac69 100644 --- a/include/internal/catch_vs_test_registry.hpp +++ b/include/internal/catch_vs_test_registry.hpp @@ -16,27 +16,11 @@ #include "catch_common.h" #include "catch_interfaces_testcase.h" #include "internal/catch_compiler_capabilities.h" +#include "internal/clara.h" #include namespace Catch { - typedef void(*TestFunction)(); - - class FreeFunctionTestCase : public SharedImpl { - public: - - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} - - virtual void invoke() const { - m_fun(); - } - - private: - virtual ~FreeFunctionTestCase(); - - TestFunction m_fun; - }; - class MethodTestCase : public SharedImpl { struct placeholder @@ -79,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 ) {} @@ -90,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 @@ -105,9 +110,38 @@ struct NameAndDesc { std::string description; }; +struct AutoReg { + + AutoReg( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + + template + AutoReg( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + registerTestCase( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); + } + + void registerTestCase( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); + + ~AutoReg(); + +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); +}; + } // 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__) ) @@ -134,7 +168,7 @@ struct NameAndDesc { #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) )] \ @@ -142,22 +176,24 @@ struct NameAndDesc { 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 @@ -173,99 +209,161 @@ struct NameAndDesc { #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); \ - Catch::TestCase tc = Catch::makeTestCase( new Catch::FreeFunctionTestCase( & Method ), "", name_desc.name, name_desc.description, CATCH_INTERNAL_LINEINFO ); \ - tr.runTest(tc); \ + std::vector testCase = Catch::getRegistryHub().getTestCaseRegistry().getMatchingTestCases(name_desc.name); \ + 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; \ - Catch::Ptr config(new Catch::Config(cd)); \ - Catch::MSTestReporter* rep = new Catch::MSTestReporter(config.get()); \ - Catch::RunContext tr(config.get(), rep); \ - Catch::TestCase tc = Catch::makeTestCase( new Catch::MethodTestCase( & ClassMethod ), # ClassMethod, name_desc.name, name_desc.description, CATCH_INTERNAL_LINEINFO ); \ - tr.runTest(tc); \ - } - -#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 ) { \ - 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 ) { \ - 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 ) { \ - 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 @@ -279,29 +377,29 @@ struct NameAndDesc { #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 @@ -320,51 +418,6 @@ struct NameAndDesc { #include "catch_exception_translator_registry.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 IReporterFactory::~IReporterFactory() {} - inline IReporterRegistry::~IReporterRegistry() {} - inline IStreamingReporter::~IStreamingReporter() {} - inline AssertionStats::~AssertionStats() {} - inline SectionStats::~SectionStats() {} - 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() {} - //TestRegistry::~TestRegistry() {} - inline FreeFunctionTestCase::~FreeFunctionTestCase() {} - inline IGeneratorInfo::~IGeneratorInfo() {} - inline IGeneratorsForTest::~IGeneratorsForTest() {} - - 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() {} - - //INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter ) -} - #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/include/reporters/catch_vs_reporter.hpp b/include/reporters/catch_vs_reporter.hpp index 29f0be57..9b7d2368 100644 --- a/include/reporters/catch_vs_reporter.hpp +++ b/include/reporters/catch_vs_reporter.hpp @@ -46,15 +46,27 @@ namespace Catch { #endif // detect CLR struct MSTestReporter : SharedImpl { + MSTestReporter( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + m_headerPrinted( 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."); + }*/ } } @@ -120,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() ); @@ -141,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(); @@ -448,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/MessageInstantiationTests1.cpp b/projects/SelfTest/MessageInstantiationTests1.cpp index c4605cc2..9939d274 100644 --- a/projects/SelfTest/MessageInstantiationTests1.cpp +++ b/projects/SelfTest/MessageInstantiationTests1.cpp @@ -15,21 +15,24 @@ namespace Counter { int g_haveCountedMessages = 0; } -// This test works with the equivalent in MessageInstantiationTests2.cpp -// The first test to reach this point will increment the MessageInfo counter. Subsequent tests -// just check the value. The purpose of this test is to verify that the compiler's -// greedy instantiation (or whatever process it uses) eliminate all other -// references to the globalCount - -TEST_CASE("message counting1","") +namespace MI1 { - if( Counter::g_haveCountedMessages > 0 ) { - REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); - } - else + // This test works with the equivalent in MessageInstantiationTests2.cpp + // The first test to reach this point will increment the MessageInfo counter. Subsequent tests + // just check the value. The purpose of this test is to verify that the compiler's + // greedy instantiation (or whatever process it uses) eliminate all other + // references to the globalCount + + TEST_CASE("message counting1","") { - ++Catch::MessageInfoCounter::globalCount; - Counter::g_haveCountedMessages = 1; + if( Counter::g_haveCountedMessages > 0 ) { + REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); + } + else + { + ++Catch::MessageInfoCounter::globalCount; + Counter::g_haveCountedMessages = 1; + } } } @@ -37,14 +40,17 @@ namespace LongCounter { int g_haveCountedMessagesLong = 0; } -TEST_CASE("long message counting1","") +namespace MI1 { - if( LongCounter::g_haveCountedMessagesLong > 0 ) { - REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); - } - else + TEST_CASE("long message counting1","") { - ++Catch::MessageInfoCounter::globalCount; - LongCounter::g_haveCountedMessagesLong = 1; + if( LongCounter::g_haveCountedMessagesLong > 0 ) { + REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); + } + else + { + ++Catch::MessageInfoCounter::globalCount; + LongCounter::g_haveCountedMessagesLong = 1; + } } } diff --git a/projects/SelfTest/MessageInstantiationTests2.cpp b/projects/SelfTest/MessageInstantiationTests2.cpp index 45a99f19..e28e49a5 100644 --- a/projects/SelfTest/MessageInstantiationTests2.cpp +++ b/projects/SelfTest/MessageInstantiationTests2.cpp @@ -14,21 +14,24 @@ namespace Counter { extern int g_haveCountedMessages; } -// This test works with the equivalent in MessageInstantiationTests1.cpp -// The first test to reach this point will increment the MessageInfo counter. Subsequent tests -// just check the value. The purpose of this test is to verify that the compiler's -// greedy instantiation (or whatever process it uses) eliminate all other -// references to the globalCount - -TEST_CASE("message counting2","") +namespace MI2 { - if( Counter::g_haveCountedMessages > 0 ) { - REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); - } - else + // This test works with the equivalent in MessageInstantiationTests1.cpp + // The first test to reach this point will increment the MessageInfo counter. Subsequent tests + // just check the value. The purpose of this test is to verify that the compiler's + // greedy instantiation (or whatever process it uses) eliminate all other + // references to the globalCount + + TEST_CASE("message counting2","") { - ++Catch::MessageInfoCounter::globalCount; - Counter::g_haveCountedMessages = 1; + if( Counter::g_haveCountedMessages > 0 ) { + REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); + } + else + { + ++Catch::MessageInfoCounter::globalCount; + Counter::g_haveCountedMessages = 1; + } } } @@ -36,14 +39,17 @@ namespace LongCounter { extern int g_haveCountedMessagesLong; } -TEST_CASE("long message counting2","") +namespace MI2 { - if( LongCounter::g_haveCountedMessagesLong > 0 ) { - REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); - } - else + TEST_CASE("long message counting2","") { - ++Catch::MessageInfoCounter::globalCount; - LongCounter::g_haveCountedMessagesLong = 1; + if( LongCounter::g_haveCountedMessagesLong > 0 ) { + REQUIRE( Catch::MessageInfoCounter::globalCount > 0 ); + } + else + { + ++Catch::MessageInfoCounter::globalCount; + LongCounter::g_haveCountedMessagesLong = 1; + } } } 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 4d3edcef..0b8ede82 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -346,35 +346,35 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { 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 ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 80 ) ).toString() == testString ); + CHECK( Catch::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" ); + 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( 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" ); + 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( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); - CHECK( Text( testString, TextAttributes().setWidth( 5 ) ).toString() == "one\ntwo\nthree\nfour" ); + 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( 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( 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" ); - 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" ); + 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", "" ) { - Text text( testString, TextAttributes().setWidth( 6 ) ); + Catch::Text text( testString, TextAttributes().setWidth( 6 ) ); REQUIRE( text.size() == 4 ); CHECK( text[0] == "one" ); CHECK( text[1] == "two" ); @@ -382,7 +382,7 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { CHECK( text[3] == "four" ); } SECTION( "Indent first line differently", "" ) { - Text text( testString, TextAttributes() + Catch::Text text( testString, TextAttributes() .setWidth( 10 ) .setIndent( 4 ) .setInitialIndent( 1 ) ); @@ -397,22 +397,22 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { 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 ); + 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( 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" ); + 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( 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" ); + 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( Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); + CHECK( Catch::Text( testString, TextAttributes().setWidth( 6 ) ).toString() == "one\ntwo\nthree\nfour" ); } } @@ -421,7 +421,7 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { // guide: 1234567890123456789 std::string testString = "one two \tthree four five six"; - CHECK( Text( testString, TextAttributes().setWidth( 15 ) ).toString() + CHECK( Catch::Text( testString, TextAttributes().setWidth( 15 ) ).toString() == "one two three\n four\n five\n six" ); } @@ -522,12 +522,12 @@ TEST_CASE( "Strings can be rendered with colour", "[colour]" ) { TEST_CASE( "Text can be formatted using the Text class", "" ) { - CHECK( Text( "hi there" ).toString() == "hi there" ); + CHECK( Catch::Text( "hi there" ).toString() == "hi there" ); TextAttributes narrow; narrow.setWidth( 6 ); - CHECK( Text( "hi there", narrow ).toString() == "hi\nthere" ); + CHECK( Catch::Text( "hi there", narrow ).toString() == "hi\nthere" ); } TEST_CASE( "Long text is truncted", "[Text][Truncated]" ) { @@ -537,7 +537,7 @@ TEST_CASE( "Long text is truncted", "[Text][Truncated]" ) { std::ostringstream oss; for(int i = 0; i < 600; ++i ) oss << longLine << longLine << "\n"; - Text t( oss.str() ); + Catch::Text t( oss.str() ); CHECK_THAT( t.toString(), EndsWith( "... message truncated due to excessive size" ) ); } diff --git a/projects/SelfTest/TrickyTests.cpp b/projects/SelfTest/TrickyTests.cpp index d685bfaf..acb1a611 100644 --- a/projects/SelfTest/TrickyTests.cpp +++ b/projects/SelfTest/TrickyTests.cpp @@ -24,7 +24,7 @@ namespace Catch } } -namespace TrickTests +namespace TrickyTests { /////////////////////////////////////////////////////////////////////////////// TEST_CASE @@ -209,177 +209,180 @@ namespace ObjectWithNonConstEqualityOperator } } -namespace EnumBitFieldTests +namespace TrickyTests { - enum Bits {bit0 = 0x0001, bit1 = 0x0002, bit2 = 0x0004, bit3 = 0x0008, bit1and2 = 0x0006, - bit30 = 0x40000000, bit31 = 0x80000000, - bit30and31 = 0xc0000000}; - - TEST_CASE( "Test enum bit values", "[Tricky]" ) + namespace EnumBitFieldTests { - REQUIRE( 0xc0000000 == bit30and31 ); - } -} + enum Bits {bit0 = 0x0001, bit1 = 0x0002, bit2 = 0x0004, bit3 = 0x0008, bit1and2 = 0x0006, + bit30 = 0x40000000, bit31 = 0x80000000, + bit30and31 = 0xc0000000}; -struct Obj -{ - Obj():prop(&p){} + TEST_CASE( "Test enum bit values", "[Tricky]" ) + { + REQUIRE( 0xc0000000 == bit30and31 ); + } + } + + struct Obj + { + Obj():prop(&p){} - int p; - int* prop; -}; + int p; + int* prop; + }; -TEST_CASE("boolean member", "[Tricky]") -{ - Obj obj; - REQUIRE( obj.prop != NULL ); -} - -// Tests for a problem submitted by Ralph McArdell -// -// The static bool value should not need to be defined outside the -// struct it is declared in - but when evaluating it in a deduced -// context it appears to require the extra definition. -// The issue was fixed by adding bool overloads to bypass the -// templates that were there to deduce it. -template -struct is_true -{ - static const bool value = B; -}; - -TEST_CASE( "(unimplemented) static bools can be evaluated", "[Tricky]" ) -{ - SECTION("compare to true","") + TEST_CASE("boolean member", "[Tricky]") { - REQUIRE( is_true::value == true ); - REQUIRE( true == is_true::value ); - } - SECTION("compare to false","") - { - REQUIRE( is_true::value == false ); - REQUIRE( false == is_true::value ); + Obj obj; + REQUIRE( obj.prop != NULL ); } - SECTION("negation", "") + // Tests for a problem submitted by Ralph McArdell + // + // The static bool value should not need to be defined outside the + // struct it is declared in - but when evaluating it in a deduced + // context it appears to require the extra definition. + // The issue was fixed by adding bool overloads to bypass the + // templates that were there to deduce it. + template + struct is_true { - REQUIRE( !is_true::value ); + static const bool value = B; + }; + + TEST_CASE( "(unimplemented) static bools can be evaluated", "[Tricky]" ) + { + SECTION("compare to true","") + { + REQUIRE( is_true::value == true ); + REQUIRE( true == is_true::value ); + } + SECTION("compare to false","") + { + REQUIRE( is_true::value == false ); + REQUIRE( false == is_true::value ); + } + + SECTION("negation", "") + { + REQUIRE( !is_true::value ); + } + + SECTION("double negation","") + { + REQUIRE( !!is_true::value ); + } + + SECTION("direct","") + { + REQUIRE( is_true::value ); + REQUIRE_FALSE( is_true::value ); + } } - SECTION("double negation","") + // Uncomment these tests to produce an error at test registration time + /* + TEST_CASE( "Tests with the same name are not allowed", "[Tricky]" ) { - REQUIRE( !!is_true::value ); - } - - SECTION("direct","") - { - REQUIRE( is_true::value ); - REQUIRE_FALSE( is_true::value ); - } -} - -// Uncomment these tests to produce an error at test registration time -/* -TEST_CASE( "Tests with the same name are not allowed", "[Tricky]" ) -{ -} -TEST_CASE( "Tests with the same name are not allowed", "[Tricky]" ) -{ + } + TEST_CASE( "Tests with the same name are not allowed", "[Tricky]" ) + { -} -*/ + } + */ -struct Boolable -{ - explicit Boolable( bool value ) : m_value( value ) {} + struct Boolable + { + explicit Boolable( bool value ) : m_value( value ) {} - operator Catch::SafeBool::type() const { - return Catch::SafeBool::makeSafe( m_value ); + operator Catch::SafeBool::type() const { + return Catch::SafeBool::makeSafe( m_value ); + } + + bool m_value; + }; + + TEST_CASE( "Objects that evaluated in boolean contexts can be checked", "[Tricky][SafeBool]" ) + { + Boolable True( true ); + Boolable False( false ); + + CHECK( True ); + CHECK( !False ); + CHECK_FALSE( False ); } - bool m_value; -}; - -TEST_CASE( "Objects that evaluated in boolean contexts can be checked", "[Tricky][SafeBool]" ) -{ - Boolable True( true ); - Boolable False( false ); - - CHECK( True ); - CHECK( !False ); - CHECK_FALSE( False ); -} - -TEST_CASE( "Assertions then sections", "[Tricky]" ) -{ - // This was causing a failure due to the way the console reporter was handling - // the current section - - REQUIRE( Catch::isTrue( true ) ); - - SECTION( "A section", "" ) + TEST_CASE( "Assertions then sections", "[Tricky]" ) { + // This was causing a failure due to the way the console reporter was handling + // the current section + REQUIRE( Catch::isTrue( true ) ); + + SECTION( "A section", "" ) + { + REQUIRE( Catch::isTrue( true ) ); - SECTION( "Another section", "" ) - { - REQUIRE( Catch::isTrue( true ) ); - } - SECTION( "Another other section", "" ) - { - REQUIRE( Catch::isTrue( true ) ); + SECTION( "Another section", "" ) + { + REQUIRE( Catch::isTrue( true ) ); + } + SECTION( "Another other section", "" ) + { + REQUIRE( Catch::isTrue( true ) ); + } } } + + struct Awkward + { + operator int() const { return 7; } + }; + + TEST_CASE( "non streamable - with conv. op", "[Tricky]" ) + { + Awkward awkward; + std::string s = Catch::toString( awkward ); + REQUIRE( s == "7" ); + } + + inline void foo() {} + + typedef void (*fooptr_t)(); + + TEST_CASE( "Comparing function pointers", "[Tricky][function pointer]" ) + { + // This was giving a warning in VS2010 + // #179 + fooptr_t a = foo; + + REQUIRE( a ); + REQUIRE( a == &foo ); + } + + class ClassName {}; + + TEST_CASE( "pointer to class", "[Tricky]" ) + { + ClassName *p = 0; + REQUIRE( p == 0 ); + } + + #ifdef CATCH_CONFIG_CPP11_NULLPTR + + #include + + TEST_CASE( "null_ptr", "[Tricky]" ) + { + std::unique_ptr ptr; + REQUIRE(ptr.get() == nullptr); + } + + #endif + + TEST_CASE( "X/level/0/a", "[Tricky]" ) { SUCCEED(""); } + TEST_CASE( "X/level/0/b", "[Tricky][fizz]" ){ SUCCEED(""); } + TEST_CASE( "X/level/1/a", "[Tricky]" ) { SUCCEED(""); } + TEST_CASE( "X/level/1/b", "[Tricky]" ) { SUCCEED(""); } } - -struct Awkward -{ - operator int() const { return 7; } -}; - -TEST_CASE( "non streamable - with conv. op", "[Tricky]" ) -{ - Awkward awkward; - std::string s = Catch::toString( awkward ); - REQUIRE( s == "7" ); -} - -inline void foo() {} - -typedef void (*fooptr_t)(); - -TEST_CASE( "Comparing function pointers", "[Tricky][function pointer]" ) -{ - // This was giving a warning in VS2010 - // #179 - fooptr_t a = foo; - - REQUIRE( a ); - REQUIRE( a == &foo ); -} - -class ClassName {}; - -TEST_CASE( "pointer to class", "[Tricky]" ) -{ - ClassName *p = 0; - REQUIRE( p == 0 ); -} - -#ifdef CATCH_CONFIG_CPP11_NULLPTR - -#include - -TEST_CASE( "null_ptr", "[Tricky]" ) -{ - std::unique_ptr ptr; - REQUIRE(ptr.get() == nullptr); -} - -#endif - -TEST_CASE( "X/level/0/a", "[Tricky]" ) { SUCCEED(""); } -TEST_CASE( "X/level/0/b", "[Tricky][fizz]" ){ SUCCEED(""); } -TEST_CASE( "X/level/1/a", "[Tricky]" ) { SUCCEED(""); } -TEST_CASE( "X/level/1/b", "[Tricky]" ) { SUCCEED(""); } 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/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 \ diff --git a/projects/VS2010/ManagedTestCatch/ManagedTestCatch.sln b/projects/VS2010/ManagedTestCatch/ManagedTestCatch.sln index 8d265189..7cfd3e6c 100644 --- a/projects/VS2010/ManagedTestCatch/ManagedTestCatch.sln +++ b/projects/VS2010/ManagedTestCatch/ManagedTestCatch.sln @@ -16,13 +16,19 @@ Global EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + DebugMbcs|Win32 = DebugMbcs|Win32 Release|Win32 = Release|Win32 + ReleaseMbcs|Win32 = ReleaseMbcs|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.Debug|Win32.ActiveCfg = Debug|Win32 {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.Debug|Win32.Build.0 = Debug|Win32 + {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.DebugMbcs|Win32.ActiveCfg = DebugMbcs|Win32 + {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.DebugMbcs|Win32.Build.0 = DebugMbcs|Win32 {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.Release|Win32.ActiveCfg = Release|Win32 {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.Release|Win32.Build.0 = Release|Win32 + {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.ReleaseMbcs|Win32.ActiveCfg = ReleaseMbcs|Win32 + {7CC06A6B-763E-42B3-AF6C-8F1E340372A1}.ReleaseMbcs|Win32.Build.0 = ReleaseMbcs|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj b/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj index b14d3a5f..1dd4095e 100644 --- a/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj +++ b/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj @@ -1,17 +1,25 @@  + + DebugMbcs + Win32 + Debug Win32 + + ReleaseMbcs + Win32 + Release Win32 - DefaultTest + $(ProjectName) {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} {7CC06A6B-763E-42B3-AF6C-8F1E340372A1} ManagedCProj @@ -23,24 +31,42 @@ true Unicode + + DynamicLibrary + true + MultiByte + DynamicLibrary Safe Unicode + + DynamicLibrary + Safe + MultiByte + + + + + + + if exist app.config copy app.config "$(OutDir)app.config" true + true false + false @@ -58,6 +84,22 @@ + + + Level4 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + NotUsing + ..\..\..\include + true + MultiThreadedDebugDLL + + + true + + + + Level3 @@ -72,6 +114,20 @@ + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + NotUsing + true + ../../../include + + + true + + + + @@ -90,13 +146,18 @@ + + + Create + Create Create + Create diff --git a/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj.filters b/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj.filters index 0f1c9922..e81eb51c 100644 --- a/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj.filters +++ b/projects/VS2010/ManagedTestCatch/ManagedTestCatch.vcxproj.filters @@ -63,6 +63,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/projects/VS2012/ManagedTestCatch/ManagedTestCatch.sln b/projects/VS2012/ManagedTestCatch/ManagedTestCatch.sln index b41b17a4..a0be7336 100644 --- a/projects/VS2012/ManagedTestCatch/ManagedTestCatch.sln +++ b/projects/VS2012/ManagedTestCatch/ManagedTestCatch.sln @@ -6,13 +6,19 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + DebugMbcs|Win32 = DebugMbcs|Win32 Release|Win32 = Release|Win32 + ReleaseMbcs|Win32 = ReleaseMbcs|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9757CB21-B840-49A6-B057-9F322E543DD6}.Debug|Win32.ActiveCfg = Debug|Win32 {9757CB21-B840-49A6-B057-9F322E543DD6}.Debug|Win32.Build.0 = Debug|Win32 + {9757CB21-B840-49A6-B057-9F322E543DD6}.DebugMbcs|Win32.ActiveCfg = DebugMbcs|Win32 + {9757CB21-B840-49A6-B057-9F322E543DD6}.DebugMbcs|Win32.Build.0 = DebugMbcs|Win32 {9757CB21-B840-49A6-B057-9F322E543DD6}.Release|Win32.ActiveCfg = Release|Win32 {9757CB21-B840-49A6-B057-9F322E543DD6}.Release|Win32.Build.0 = Release|Win32 + {9757CB21-B840-49A6-B057-9F322E543DD6}.ReleaseMbcs|Win32.ActiveCfg = ReleaseMbcs|Win32 + {9757CB21-B840-49A6-B057-9F322E543DD6}.ReleaseMbcs|Win32.Build.0 = ReleaseMbcs|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj b/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj index e5bcc201..d8e9a677 100644 --- a/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj +++ b/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj @@ -1,17 +1,25 @@  + + DebugMbcs + Win32 + Debug Win32 + + ReleaseMbcs + Win32 + Release Win32 - DefaultTest + $(ProjectName) {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} {9757CB21-B840-49A6-B057-9F322E543DD6} ManagedCProj @@ -24,30 +32,54 @@ Safe Unicode - + DynamicLibrary v110 Safe + MultiByte + + + DynamicLibrary + v110 + true Unicode + + DynamicLibrary + v110 + true + MultiByte + + + + + + + if exist app.config copy app.config "$(OutDir)app.config" true + + true + false + + false + Level3 @@ -66,6 +98,25 @@ taskkill /F /IM vstest.executionengine.x86.exe /FI "MEMUSAGE gt 1" + + + Level3 + Disabled + WIN32;_DEBUG;%(PreprocessorDefinitions) + NotUsing + ../../../include + true + MultiThreadedDebugDLL + + + true + + + + + taskkill /F /IM vstest.executionengine.x86.exe /FI "MEMUSAGE gt 1" + + Level3 @@ -79,6 +130,20 @@ + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + NotUsing + ../../../include + true + + + true + + + + ..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll @@ -100,13 +165,18 @@ + + + Create + Create Create + Create diff --git a/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj.filters b/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj.filters index f32e36cb..2db549a5 100644 --- a/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj.filters +++ b/projects/VS2012/ManagedTestCatch/ManagedTestCatch.vcxproj.filters @@ -63,6 +63,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/projects/VS2012/NativeTestCatch/NativeTestCatch.sln b/projects/VS2012/NativeTestCatch/NativeTestCatch.sln index 18ec7dc0..9e250bd9 100644 --- a/projects/VS2012/NativeTestCatch/NativeTestCatch.sln +++ b/projects/VS2012/NativeTestCatch/NativeTestCatch.sln @@ -6,13 +6,19 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + DebugMbcs|Win32 = DebugMbcs|Win32 Release|Win32 = Release|Win32 + ReleaseMbcs|Win32 = ReleaseMbcs|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {977CE524-3FC7-4281-9C1B-77C210F24A9B}.Debug|Win32.ActiveCfg = Debug|Win32 {977CE524-3FC7-4281-9C1B-77C210F24A9B}.Debug|Win32.Build.0 = Debug|Win32 + {977CE524-3FC7-4281-9C1B-77C210F24A9B}.DebugMbcs|Win32.ActiveCfg = DebugMbcs|Win32 + {977CE524-3FC7-4281-9C1B-77C210F24A9B}.DebugMbcs|Win32.Build.0 = DebugMbcs|Win32 {977CE524-3FC7-4281-9C1B-77C210F24A9B}.Release|Win32.ActiveCfg = Release|Win32 {977CE524-3FC7-4281-9C1B-77C210F24A9B}.Release|Win32.Build.0 = Release|Win32 + {977CE524-3FC7-4281-9C1B-77C210F24A9B}.ReleaseMbcs|Win32.ActiveCfg = ReleaseMbcs|Win32 + {977CE524-3FC7-4281-9C1B-77C210F24A9B}.ReleaseMbcs|Win32.Build.0 = ReleaseMbcs|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj b/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj index 87bdb6d4..afc168fa 100644 --- a/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj +++ b/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj @@ -1,10 +1,18 @@  + + DebugMbcs + Win32 + Debug Win32 + + ReleaseMbcs + Win32 + Release Win32 @@ -23,6 +31,20 @@ Unicode false + + DynamicLibrary + true + v110 + MultiByte + false + + + DynamicLibrary + true + v110 + MultiByte + false + DynamicLibrary false @@ -37,6 +59,12 @@ + + + + + + @@ -44,6 +72,12 @@ true + + true + + + true + true @@ -62,6 +96,36 @@ $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + NotUsing + Level3 + Disabled + ../../../include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + Disabled + ../../../include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + Level3 @@ -97,11 +161,16 @@ + + + Create + Create + Create Create diff --git a/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj.filters b/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj.filters index 24538c37..1b5c5e7e 100644 --- a/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj.filters +++ b/projects/VS2012/NativeTestCatch/NativeTestCatch.vcxproj.filters @@ -50,12 +50,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -68,5 +62,20 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/scripts/approvalVSTests.py b/scripts/approvalVSTests.py new file mode 100644 index 00000000..bef1cb79 --- /dev/null +++ b/scripts/approvalVSTests.py @@ -0,0 +1,716 @@ +#!/c/Python27/python + +import os +import sys +import subprocess +import re + +import xml.etree.cElementTree as etree + +from scriptCommon import catchPath +#from rawfile import writeRawFile +#from rawfile import parseRawFileIntoTree +from catch_test_run import TestRunApprovedHandler +from catch_test_run import TestRunData +from catch_test_run import TestRunResultHandler +from catch_test_case import TestCaseResultParser +from catch_test_case import TestCaseData + +rootPath = os.path.join(os.path.join(os.path.join( catchPath, 'projects'), 'SelfTest'), 'Baselines' ) + +if len(sys.argv) == 2: + cmdPath = sys.argv[1] +else: + if sys.platform == 'win32': + cmdPath = os.path.join( catchPath, 'projects\\VS2010\\TestCatch\\Release\\TestCatch.exe' ) + dllPath = os.path.join( catchPath, 'projects\\VS2010\\ManagedTestCatch\\Release\\ManagedTestCatch.dll' ) + else: + cmdPath = os.path.join( catchPath, 'projects/XCode4/CatchSelfTest/DerivedData/CatchSelfTest/Build/Products/Debug/CatchSelfTest' ) + +print cmdPath + +overallResult = 0 + +def approve( baseName, args ): + global overallResult + args[0:0] = [cmdPath] + baselinesPath = os.path.join( rootPath, '{0}.approved.txt'.format( baseName ) ) + baselinesSortedPath = os.path.join( rootPath, '{0}.sorted.approved.txt'.format( baseName ) ) + rawResultsPath = os.path.join( rootPath, '_{0}.tmp'.format( baseName ) ) + if os.path.exists( baselinesPath ): + approvedFileHandler = TestRunApprovedHandler(baselinesPath) + baselinesPathNew = os.path.join( rootPath, '{0}.approved.new.txt'.format( baseName ) ) + approvedFileHandler.writeRawFile(baselinesPathNew) + approvedFileHandler.writeSortedRawFile(baselinesSortedPath) + else: + raise Exception("Base file does not exist: '" + baselinesPath + "'") + + if not(os.path.exists( args[0] )): + raise Exception("Executable does not exist: '" + args[0] + "'") + + f = open( rawResultsPath, 'w' ) + subprocess.call( args, stdout=f, stderr=f ) + f.close() + + if os.path.exists( rawResultsPath ): + resultFileHandler = TestRunResultHandler(rawResultsPath) + rawPathNew = os.path.join( rootPath, '{0}.rewrite.txt'.format( baseName ) ) + #print "F:",rawPathNew,",",approvedFileHandler.current.outputLine + resultFileHandler.writeRawFile(rawPathNew) + rawPathNewSorted = os.path.join( rootPath, '{0}.sorted.unapproved.txt'.format( baseName ) ) + resultFileHandler.writeSortedUnapprovedFile(rawPathNewSorted, approvedFileHandler.current.outputLine) + else: + raise Exception("Results file does not exist: '" + rawResultsPath + "'") + + #os.remove( rawResultsPath ) + print + print baseName + ":" + if os.path.exists( baselinesSortedPath ) and os.path.exists( rawPathNewSorted ): + diffResult = subprocess.call([ "diff", "--ignore-all-space", baselinesSortedPath, rawPathNewSorted ] ) + if diffResult == 0: + #os.remove( filteredResultsPath ) + if not(sys.platform == 'win32'): + print " \033[92mResults matched" + else: + print " Results matched" + else: + if not(sys.platform == 'win32'): + print " \n****************************\n \033[91mResults differed" + else: + print " \n****************************\n Results differed" + if diffResult > overallResult: + overallResult = diffResult + if not(sys.platform == 'win32'): + print "\033[0m" + +def approveJunit( baseName, args ): + global overallResult + args[0:0] = [cmdPath] + baselinesPath = os.path.join( rootPath, '{0}.approved.txt'.format( baseName ) ) + baselinesSortedPath = os.path.join( rootPath, '{0}.sorted.approved.txt'.format( baseName ) ) + #baselinesFixedPath = os.path.join( rootPath, '{0}.rewrite.approved.txt'.format( baseName ) ) + rawResultsPath = os.path.join( rootPath, '_{0}.tmp'.format( baseName ) ) + if os.path.exists( baselinesPath ): + xml = "" + f = open( baselinesPath, 'r' ) + for line in f: + xml += line + xml = xml.replace("", "<line number>") + xml = xml.replace("", "<hex digits>") + #f2 = open( baselinesFixedPath, 'wb' ) + #f2.write(xml) + #f2.close() + + # ClassTests.cpp: + otherApprovedTestParser = re.compile( r'(.*\..pp).*:<(.*).*>' ) + testRun = TestRunData() + testcase = None + root = etree.fromstring(xml) + for testsuites in root: + if testsuites.tag == "testsuite": + testRun = TestRunData() + testRun.appname = testsuites.get("name") + testRun.errors = testsuites.get("errors") + testRun.failures = testsuites.get("failures") + testRun.tests = testsuites.get("tests") + for tc in testsuites: + if tc.tag == "testcase": + cls = tc.get("classname") + #print "C:",cls,tc + if len(cls): + testcase = testRun.addClassTestCase(cls, tc.get("name")) + else: + testcase = testRun.addTestCase(tc.get("name")) + for prop in tc: + if prop.tag == "failure": + text = prop.text.strip() + lines = text.splitlines() + filename = "" + lineNumber = "" + output = [] + for l in lines: + m = otherApprovedTestParser.match(l) + if m: + filename = m.group(1) + lineNumber = m.group(2) + else: + output.append(l) + testcase.addFailure(filename, lineNumber, output, prop.get("message"), prop.get("type")) + elif prop.tag == "error": + text = prop.text.strip() + lines = text.splitlines() + filename = "" + lineNumber = "" + output = [] + for l in lines: + m = otherApprovedTestParser.match(l) + if m: + filename = m.group(1) + lineNumber = m.group(2) + else: + output.append(l) + testcase.addError(filename, lineNumber, output, prop.get("message"), prop.get("type")) + elif prop.tag == "system-out": + text = prop.text.strip() + lines = text.splitlines() + testcase.addSysout(lines) + elif prop.tag == "system-err": + text = prop.text.strip() + lines = text.splitlines() + testcase.addSyserr(lines) + elif tc.tag == "system-out": + text = tc.text.strip() + lines = text.splitlines() + testRun.addSysout(lines) + elif tc.tag == "system-err": + text = tc.text.strip() + lines = text.splitlines() + testRun.addSyserr(lines) + else: + print tc.tag + + lines = testRun.generateSortedUnapprovedJunit() + + rawWriteFile = open( baselinesSortedPath, 'wb' ) + for line in lines: + #print "L:",line + rawWriteFile.write(line + "\n") + rawWriteFile.close() + + if not(os.path.exists( args[0] )): + raise Exception("Executable does not exist: '" + args[0] + "'") + + f = open( rawResultsPath, 'w' ) + subprocess.call( args, stdout=f, stderr=f ) + f.close() + + rawSortedPath = os.path.join( rootPath, '{0}.sorted.unapproved.txt'.format( baseName ) ) + if os.path.exists( rawResultsPath ): + xml = "" + f = open( rawResultsPath, 'r' ) + for line in f: + xml += line + #xml = xml.replace("", "<line number>") + #xml = xml.replace("", "<hex digits>") + + # ClassTests.cpp: + otherResultsTestParser = re.compile( r'(.*\\)(.*\..pp).*\((.*).*\)' ) + testRun = TestRunData() + testcase = None + root = etree.fromstring(xml) + for testsuites in root: + if testsuites.tag == "testsuite": + testRun = TestRunData() + testRun.appname = testsuites.get("name") + testRun.errors = testsuites.get("errors") + testRun.failures = testsuites.get("failures") + testRun.tests = testsuites.get("tests") + for tc in testsuites: + if tc.tag == "testcase": + cls = tc.get("classname") + #print "C:",cls,tc + if len(cls): + if cls.startswith("::"): + cls = cls[2:] + testcase = testRun.addClassTestCase(cls, tc.get("name")) + else: + testcase = testRun.addTestCase(tc.get("name")) + for prop in tc: + if prop.tag == "failure": + text = prop.text.strip() + lines = text.splitlines() + filename = "" + lineNumber = "" + output = [] + for l in lines: + m = otherResultsTestParser.match(l) + if m: + filename = m.group(2) + lineNumber = "line number" + else: + output.append(l) + testcase.addFailure(filename, lineNumber, output, prop.get("message"), prop.get("type")) + elif prop.tag == "error": + text = prop.text.strip() + lines = text.splitlines() + filename = "" + lineNumber = "" + output = [] + for l in lines: + m = otherResultsTestParser.match(l) + if m: + filename = m.group(2) + lineNumber = "line number" + else: + output.append(l) + testcase.addError(filename, lineNumber, output, prop.get("message"), prop.get("type")) + elif prop.tag == "system-out": + text = prop.text.strip() + lines = text.splitlines() + testcase.addSysout(lines) + elif prop.tag == "system-err": + text = prop.text.strip() + lines = text.splitlines() + testcase.addSyserr(lines) + elif tc.tag == "system-out": + text = tc.text.strip() + lines = text.splitlines() + testRun.addSysout(lines) + elif tc.tag == "system-err": + text = tc.text.strip() + lines = text.splitlines() + testRun.addSyserr(lines) + else: + print tc.tag + + lines = testRun.generateSortedUnapprovedJunit() + + rawWriteFile = open( rawSortedPath, 'wb' ) + for line in lines: + #print "L:",line + rawWriteFile.write(line + "\n") + rawWriteFile.close() + +def addSubSection(testcase, section, exp): + r = exp.find("OverallResults") + if r != None: + ores = [] + ores.append(r.get("successes")) + ores.append(r.get("failures")) + if section == None: + section = testcase.addSection(exp.get("name"), exp.get("description"), ores) + else: + section = testcase.addSubSection(section, exp.get("name"), exp.get("description"), ores) + e1 = False + for tmp in exp: + if tmp.tag == "OverallResults": + pass + elif tmp.tag == "Exception": + filename = tmp.get("filename") + text = tmp.text + ls = text.splitlines() + testcase.addSubException(section, filename, ls) + elif tmp.tag == "Section": + addSubSection(testcase, section, tmp) + elif tmp.tag == "Failure": + text = tmp.text + ls = text.splitlines() + testcase.addSubFailure(section, ls) + elif tmp.tag == "Expression": + #print "Exp:",tmp + e1 = True + result = tmp.get("success") + filename = tmp.get("filename") + subSection = testcase.addSubExpression(section,result,filename) + subExp = [] + for cond in tmp: + if cond.tag == "Original": + text = cond.text + ls = text.splitlines() + subExp.append(ls) + elif cond.tag == "Expanded" and len(subExp) == 1: + text = cond.text + ls = text.splitlines() + subExp.append(ls) + elif cond.tag == "Exception" and len(subExp) == 2: + subExp.append(cond.get("filename")) + text = cond.text + ls = text.splitlines() + subExp.append(ls) + else: + print "SX:",cond.tag + if len(subExp) >= 2: + testcase.addExpressionDetails(subSection, subExp) + else: + print "Z:",tmp.tag + #if e1: + # print "Section:",section + +def addResultsSubSection(otherResultsTestParser, testcase, section, exp): + r = exp.find("OverallResults") + if r != None: + ores = [] + ores.append(r.get("successes")) + ores.append(r.get("failures")) + if section == None: + section = testcase.addSection(exp.get("name"), exp.get("description"), ores) + else: + section = testcase.addSubSection(section, exp.get("name"), exp.get("description"), ores) + e1 = False + for tmp in exp: + if tmp.tag == "OverallResults": + pass + elif tmp.tag == "Exception": + filename = tmp.get("filename") + m = otherResultsTestParser.match(filename) + if m: + filename = "/Users/philnash/Dev/OSS/Catch/projects/SelfTest/" + m.group(2) + text = tmp.text + ls = text.splitlines() + testcase.addSubException(section, filename, ls) + elif tmp.tag == "Section": + addResultsSubSection(otherResultsTestParser, testcase, section, tmp) + elif tmp.tag == "Failure": + text = tmp.text + ls = text.splitlines() + testcase.addSubFailure(section, ls) + elif tmp.tag == "Expression": + #print "Exp:",tmp + e1 = True + result = tmp.get("success") + filename = tmp.get("filename") + m = otherResultsTestParser.match(filename) + if m: + filename = "/Users/philnash/Dev/OSS/Catch/projects/SelfTest/" + m.group(2) + subSection = testcase.addSubExpression(section,result,filename) + subExp = [] + for cond in tmp: + if cond.tag == "Original": + text = cond.text + ls = text.splitlines() + subExp.append(ls) + elif cond.tag == "Expanded" and len(subExp) == 1: + text = cond.text + ls = text.splitlines() + subExp.append(ls) + elif cond.tag == "Exception" and len(subExp) == 2: + filename = cond.get("filename") + m = otherResultsTestParser.match(filename) + if m: + filename = "/Users/philnash/Dev/OSS/Catch/projects/SelfTest/" + m.group(2) + subExp.append(filename) + text = cond.text + ls = text.splitlines() + subExp.append(ls) + else: + print "SX:",cond.tag + if len(subExp) >= 2: + testcase.addExpressionDetails(subSection, subExp) + else: + print "Z:",tmp.tag + #if e1: + # print "Section:",section + +def approveXml( baseName, args ): + global overallResult + args[0:0] = [cmdPath] + baselinesPath = os.path.join( rootPath, '{0}.approved.txt'.format( baseName ) ) + baselinesSortedPath = os.path.join( rootPath, '{0}.sorted.approved.txt'.format( baseName ) ) + #baselinesFixedPath = os.path.join( rootPath, '{0}.rewrite.approved.txt'.format( baseName ) ) + rawResultsPath = os.path.join( rootPath, '_{0}.tmp'.format( baseName ) ) + if os.path.exists( baselinesPath ): + xml = "" + f = open( baselinesPath, 'r' ) + for line in f: + xml += line + xml = xml.replace("", "<hex digits>") + + #otherApprovedTestParser = re.compile( r'(.*\..pp).*:<(.*).*>' ) + testRun = TestRunData() + testcase = None + root = etree.fromstring(xml) + testRun.appname = root.get("name") + for ts in root: + #print ts.tag + for tc in ts: + if tc.tag == "TestCase": + testcase = testRun.addTestCase(tc.get("name")) + for exp in tc: + if exp.tag == "Expression": + result = exp.get("success") + filename = exp.get("filename") + section = testcase.addExpression(result,filename) + subExp = [] + for cond in exp: + if cond.tag == "Original": + text = cond.text + ls = text.splitlines() + subExp.append(ls) + elif cond.tag == "Expanded" and len(subExp) == 1: + text = cond.text + ls = text.splitlines() + subExp.append(ls) + elif cond.tag == "Exception" and len(subExp) == 2: + subExp.append(cond.get("filename")) + text = cond.text + ls = text.splitlines() + subExp.append(ls) + else: + print "X:",cond.tag + if len(subExp) >= 2: + testcase.addExpressionDetails(section, subExp) + elif exp.tag == "Exception": + filename = exp.get("filename") + text = exp.text + ls = text.splitlines() + section = testcase.addException(filename,ls) + elif exp.tag == "Section": + addSubSection(testcase, None, exp) + elif exp.tag == "Info": + text = exp.text + ls = text.splitlines() + section = testcase.addInfo(ls) + elif exp.tag == "Warning": + text = exp.text + ls = text.splitlines() + section = testcase.addWarning(ls) + elif exp.tag == "Failure": + text = exp.text + ls = text.splitlines() + section = testcase.addSimpleFailure(ls) + elif exp.tag == "OverallResult": + testcase.addOverallResult(exp.get("success")) + else: + print "E:",exp.tag + elif tc.tag == "OverallResults": + testRun.tests = tc.get("successes") + testRun.failures = tc.get("failures") + else: + print "U:",tc.tag + + lines = testRun.generateSortedUnapprovedXml() + + rawWriteFile = open( baselinesSortedPath, 'wb' ) + for line in lines: + #print "L:",line + rawWriteFile.write(line + "\n") + rawWriteFile.close() + + if not(os.path.exists( args[0] )): + raise Exception("Executable does not exist: '" + args[0] + "'") + + f = open( rawResultsPath, 'w' ) + subprocess.call( args, stdout=f, stderr=f ) + f.close() + + rawSortedPath = os.path.join( rootPath, '{0}.sorted.unapproved.txt'.format( baseName ) ) + if os.path.exists( rawResultsPath ): + xml = "" + f = open( rawResultsPath, 'r' ) + for line in f: + xml += line + #xml = xml.replace("", "<hex digits>") + + otherResultsTestParser = re.compile( r'(.*\\)(.*\..pp)' ) + hexParser = re.compile( r'(.*)\b(0[xX][0-9a-fA-F]+)\b(.*)' ) + testRun = TestRunData() + testcase = None + root = etree.fromstring(xml) + testRun.appname = root.get("name") + if testRun.appname == "TestCatch.exe": + testRun.appname = "CatchSelfTest" + for ts in root: + #print ts.tag + for tc in ts: + if tc.tag == "TestCase": + testcase = testRun.addTestCase(tc.get("name")) + for exp in tc: + if exp.tag == "Expression": + result = exp.get("success") + filename = exp.get("filename") + m = otherResultsTestParser.match(filename) + if m: + filename = "/Users/philnash/Dev/OSS/Catch/projects/SelfTest/" + m.group(2) + section = testcase.addExpression(result,filename) + subExp = [] + for cond in exp: + if cond.tag == "Original": + text = cond.text + tmp = text.splitlines() + ls = [] + for li in tmp: + m = hexParser.match(li) + if m: + while m: + #print li, m.group(1), m.group(3) + li = m.group(1) + "0x" + m.group(3) + m = hexParser.match(li) + ls.append(li) + subExp.append(ls) + elif cond.tag == "Expanded" and len(subExp) == 1: + text = cond.text + tmp = text.splitlines() + ls = [] + for li in tmp: + m = hexParser.match(li) + if m: + while m: + #print li, m.group(1), m.group(3) + li = m.group(1) + "0x" + m.group(3) + m = hexParser.match(li) + ls.append(li) + subExp.append(ls) + elif cond.tag == "Exception" and len(subExp) == 2: + filename = cond.get("filename") + m = otherResultsTestParser.match(filename) + if m: + filename = "/Users/philnash/Dev/OSS/Catch/projects/SelfTest/" + m.group(2) + subExp.append(filename) + text = cond.text + ls = text.splitlines() + subExp.append(ls) + else: + print "X:",cond.tag + if len(subExp) >= 2: + testcase.addExpressionDetails(section, subExp) + elif exp.tag == "Exception": + filename = exp.get("filename") + m = otherResultsTestParser.match(filename) + if m: + filename = "/Users/philnash/Dev/OSS/Catch/projects/SelfTest/" + m.group(2) + text = exp.text + ls = text.splitlines() + section = testcase.addException(filename,ls) + elif exp.tag == "Section": + addResultsSubSection(otherResultsTestParser, testcase, None, exp) + elif exp.tag == "Info": + text = exp.text + ls = text.splitlines() + section = testcase.addInfo(ls) + elif exp.tag == "Warning": + text = exp.text + ls = text.splitlines() + section = testcase.addWarning(ls) + elif exp.tag == "Failure": + text = exp.text + ls = text.splitlines() + section = testcase.addSimpleFailure(ls) + elif exp.tag == "OverallResult": + testcase.addOverallResult(exp.get("success")) + else: + print "E:",exp.tag + elif tc.tag == "OverallResults": + testRun.tests = tc.get("successes") + testRun.failures = tc.get("failures") + else: + print "U:",tc.tag + + lines = testRun.generateSortedUnapprovedXml() + + rawWriteFile = open( rawSortedPath, 'wb' ) + for line in lines: + #print "L:",line + rawWriteFile.write(line + "\n") + rawWriteFile.close() + +def parseTrxFile(trxFile): + print "TRX file:" ,trxFile + if os.path.exists( trxFile ): + xml = "" + f = open( trxFile, 'r' ) + for line in f: + xml += line + + #otherResultsTestParser = re.compile( r'(.*\\)(.*\..pp)' ) + #hexParser = re.compile( r'(.*)\b(0[xX][0-9a-fA-F]+)\b(.*)' ) + testRun = TestRunData() + testRun.appname = "CatchSelfTest" + root = etree.fromstring(xml) + if testRun.appname == "TestCatch.exe": + testRun.appname = "CatchSelfTest" + qname=re.compile("{(?P.*)}(?P.*)") + ids = [] + for ts in root: + m = qname.match(ts.tag) + if m: + tag = m.group(2) + print tag + if tag != None: + if tag == "TestDefinitions": + for tc in ts: + m = qname.match(tc.tag) + if m: + tag = m.group(2) + if tag != None and tag == "UnitTest": + name = tc.get("name") + id = tc.get("id") + for item in tc: + m = qname.match(item.tag) + if m: + tag = m.group(2) + if tag != None and tag == "Description": + desc = item.text + #print desc, id + ids.append([id,desc]) + elif tag == "Results": + #print ids + ids = dict(ids) + #print ids["87ec526a-e414-1a3f-ba0f-e210b204bb42"] + resultParser = TestCaseResultParser() + for tc in ts: + m = qname.match(tc.tag) + if m: + tag = m.group(2) + if tag != None and tag == "UnitTestResult": + outcome = tc.get("outcome") + id = tc.get("testId") + if len(id) > 0: + for item in tc: + m = qname.match(item.tag) + if m: + tag = m.group(2) + if tag != None and tag == "Output": + for sub in item: + m = qname.match(sub.tag) + if m: + tag = m.group(2) + if tag != None and tag == "StdOut": + desc = sub.text + lines = desc.splitlines() + if (len(lines) > 2 and + lines[0].startswith("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") and + lines[1].startswith("Using Catch v") ): + lines = lines[2:-1] + #print "*******",desc + #print lines + for line in lines: + testcase = resultParser.parseResultLine(line) + if isinstance(testcase, TestCaseData): + testRun.testcases.append(testcase) + lines = testRun.generateSortedUnapprovedLines(0) + + rawSortedPath = os.path.join( rootPath, 'mstest.trx.sorted.unapproved.txt' ) + rawWriteFile = open( rawSortedPath, 'wb' ) + for line in lines: + #print "L:",line + rawWriteFile.write(line + "\n") + rawWriteFile.close() + + +def approveMsTest( baseName ): + rawResultsPath = os.path.join( rootPath, '_{0}.tmp'.format( baseName ) ) + if not(os.path.exists( dllPath )): + raise Exception("Managed DLL does not exist: '" + dllPath + "'") + + args = [] + args.append("MSTest.exe") + args.append("/testcontainer:" + dllPath) + #f = open( rawResultsPath, 'w' ) + #subprocess.call( args, stdout=f, stderr=f ) + #f.close() + + #if os.path.exists( rawResultsPath ): + # f = open( rawResultsPath, 'r' ) + # for line in f: + line = "Results file: c:\Projects\Catch\TestResults\NoyesMa_SACHDEW7 2013-12-09 11_43_57.trx" + + if line.startswith("Results file:"): + trxFile = line[13:].strip() + parseTrxFile(trxFile) + +# Standard console reporter +#approve( "console.std", ["~_"] ) +# console reporter, include passes, warn about No Assertions +#approve( "console.sw", ["~_", "-s", "-w", "NoAssertions"] ) +# console reporter, include passes, warn about No Assertions, limit failures to first 4 +#approve( "console.swa4", ["~_", "-s", "-w", "NoAssertions", "-x", "4"] ) +# junit reporter, include passes, warn about No Assertions +#approveJunit( "junit.sw", ["~_", "-s", "-w", "NoAssertions", "-r", "junit"] ) +# xml reporter, include passes, warn about No Assertions +#approveXml( "xml.sw", ["~_", "-s", "-w", "NoAssertions", "-r", "xml"] ) +#mstest runner, xml output +approveMsTest( "mstest.sw") + +if overallResult <> 0: + print "run approve.py to approve new baselines" +exit( overallResult) diff --git a/scripts/catch_conditions.py b/scripts/catch_conditions.py new file mode 100644 index 00000000..e7cc87b1 --- /dev/null +++ b/scripts/catch_conditions.py @@ -0,0 +1,398 @@ +import re + +class RandomOutput: + + def __init__(self): + output = [] + +class TestConditionData: + NONE = 0 + CONDITION = 1 + EXPANSION = 2 + MESSAGES = 3 + + hexParser = re.compile( r'(.*)\b(0[xX][0-9a-fA-F]+)\b(.*)' ) + + def __init__(self): + self.state = self.NONE + self.filenamePrefix = "" + self.filename = "" + self.lineNumber = "" + self.reason = "" + self.condition = "" + self.expansionPrefix = "" + self.expansion = [] + self.messagePrefix = "" + self.messages = [] + self.output = [] + self.noAssertions = "" + + def __eq__(self, other): + return (self.filenamePrefix == other.filenamePrefix and + self.filename == other.filename and self.lineNumber == other.lineNumber and + self.reason == other.reason and self.condition == other.condition and + self.expansion == other.expansion and self.messagePrefix == other.messagePrefix and + self.output == other.output and self.noAssertions == other.noAssertions) + + + def empty(self): + if self.state == self.NONE: + return True + return False + + def __repr__(self): + result = ("[" + self.reason + ", " + self.filename + ", " + self.lineNumber + ", " + self.condition + + ", " + self.expansionPrefix + ", [ ") + suffix = "" + for msg in self.expansion: + result += suffix + result += repr(msg) + suffix = ", " + result += "], " + self.messagePrefix + ", [ " + suffix = "" + for msg in self.messages: + result += suffix + result += repr(msg) + suffix = ", " + result += " ], [ " + suffix = "" + for msg in self.output: + result += suffix + result += repr(msg) + suffix = ", " + result += " ] ]" + return result + + def parseCondition(self,line): + if len(line): + if self.state == self.CONDITION and line.startswith(" "): + self.condition = line.strip() + elif self.state == self.CONDITION: + if len(self.reason) == 0 and line.startswith("PASSED:"): + self.reason = line.strip() + elif line.startswith("warning:") or line.startswith("with message"): + self.messagePrefix = line.strip() + self.state = self.MESSAGES + elif not(line.startswith(" ")): + self.expansionPrefix = line.strip() + self.state = self.EXPANSION + else: + raise Exception("Unknown condition parse line: '" + line + "'") + elif self.state == self.EXPANSION: + if line.startswith("with message"): + self.messagePrefix = line.strip() + self.state = self.MESSAGES + elif line.startswith(" "): + #print "***LINE: ", line + self.expansion.append(line[2:].rstrip()) + elif line.startswith("... message truncated due"): + #print "***MSG: ", line + self.messagePrefix = line.strip() + else: + #print "***OUTPUT: ", line + self.output.append(line.strip()) + elif self.state == self.MESSAGES: + if line.startswith(" "): + #print "***MESSAGE: ", line + self.messages.append(line.strip()) + else: + #print "***OUTPUT: ", line + self.output.append(line.strip()) + else: + raise Exception("Unknown parse line: '" + line + "'") + + if len(self.output) == 10: + if (self.output[0] == "Message from section one" and self.output[1] == "Message from section two" and + self.output[2] == "Some information" and self.output[3] == "An error" and + self.output[4] == "Message from section one" and self.output[5] == "Message from section two" and + self.output[6] == "Some information" and self.output[7] == "An error" and + self.output[8] == "hello" and self.output[9] == "hello" ): + obj = RandomOutput() + obj.output = self.output + self.output = [] + raise obj + + elif len(self.output) == 2: + if (self.output[0] == "hello" and self.output[1] == "hello" ): + obj = RandomOutput() + obj.output = self.output + self.output = [] + raise obj + + def generateApprovedLines(self): + if self.empty(): + raise Exception("Empty condition..." + repr(self)) + lines = [] + extraLine = False + if len(self.filename): + line = self.filename + ":<" + self.lineNumber + ">:" + reasonOnSameLine = False + if self.reason == "FAILED": + line += " " + "FAILED:" + reasonOnSameLine = True + lines.append(line) + if not(reasonOnSameLine) and len(self.reason): + lines.append(self.reason) + if len(self.condition): + lines.append(" " + self.condition) + if len(self.expansionPrefix): + lines.append(self.expansionPrefix) + extraLine = True + if len(self.expansion): + for line in self.expansion: + m = self.hexParser.match(line) + if m: + while m: + line = m.group(1) + "0x" + m.group(3) + m = self.hexParser.match(line) + lines.append(" " + line) + if len(self.messagePrefix): + lines.append(self.messagePrefix) + extraLine = True + if len(self.messages): + for msg in self.messages: + lines.append(" " + msg) + lines.append("") + if len(self.noAssertions) > 0: + if extraLine: + lines.append("") + lines.append(self.noAssertions) + lines.append("") + if len(self.output): + for msg in self.output: + lines.append(msg) + return lines + + def generateResultLines(self): + if self.empty(): + raise Exception("Empty condition..." + repr(self)) + lines = [] + extraLine = False + if len(self.filename): + if len(self.filenamePrefix): + line = self.filenamePrefix + self.filename + "(" + self.lineNumber + "):" + else: + line = self.filename + "(" + self.lineNumber + "):" + reasonOnSameLine = False + if self.reason == "FAILED": + line += " " + "FAILED:" + reasonOnSameLine = True + if (len(self.messagePrefix) > 0 and self.messagePrefix == "warning:" or + self.reason == "PASSED:" or self.expansionPrefix.startswith("FAILED - but was ok") ): + line += " " + lines.append(line) + if not(reasonOnSameLine) and len(self.reason): + lines.append(self.reason) + if len(self.condition): + lines.append(" " + self.condition) + if len(self.expansionPrefix): + lines.append(self.expansionPrefix) + extraLine = True + if len(self.expansion): + for line in self.expansion: + lines.append(" " + line) + if len(self.messagePrefix): + lines.append(self.messagePrefix) + extraLine = True + if len(self.messages): + for msg in self.messages: + lines.append(" " + msg) + lines.append("") + if len(self.noAssertions) > 0: + if extraLine: + lines.append("") + lines.append(self.noAssertions) + lines.append("") + if len(self.output): + for msg in self.output: + lines.append(msg) + return lines + + def generateUnapprovedLines(self): + if self.empty(): + raise Exception("Empty condition..." + repr(self)) + lines = [] + extraLine = False + if len(self.filename): + line = self.filename + ":<" + "line number" + ">:" + reasonOnSameLine = False + if self.reason == "FAILED": + line += " " + "FAILED:" + reasonOnSameLine = True + lines.append(line) + if not(reasonOnSameLine) and len(self.reason): + lines.append(self.reason) + if len(self.condition): + lines.append(" " + self.condition) + if len(self.expansionPrefix): + lines.append(self.expansionPrefix) + extraLine = True + if len(self.expansion): + for line in self.expansion: + m = self.hexParser.match(line) + if m: + while m: + line = m.group(1) + "0x" + m.group(3) + m = self.hexParser.match(line) + lines.append(" " + line) + if len(self.messagePrefix): + lines.append(self.messagePrefix) + extraLine = True + if len(self.messages): + for msg in self.messages: + lines.append(" " + msg) + lines.append("") + if len(self.noAssertions) > 0: + if extraLine: + lines.append("") + lines.append(self.noAssertions) + lines.append("") + if len(self.output): + for msg in self.output: + lines.append(msg) + return lines + + def addFailure(self, filename, lineNumber, output, message, type): + self.reason = "failure" + self.filename = filename + self.lineNumber = lineNumber + self.condition = type + if message != None: + self.expansion.append(message) + self.output = output + + def addError(self, filename, lineNumber, output, message, type): + self.reason = "error" + self.filename = filename + self.lineNumber = lineNumber + self.condition = type + if message != None: + self.expansion.append(message) + self.output = output + + def generateUnapprovedJunit(self): + lines = [] + msg = "" + for m in self.expansion: + msg += m + msg = msg.replace("\"", """) + msg = msg.replace("<", "<") + msg = msg.replace("<hex digits>", "") + #msg = msg.replace(">", ">") + + #print "R:",self.reason,msg,self.condition + if len(self.reason) > 0: + l = " <" + self.reason + if len(msg) > 0: + m = self.hexParser.match(msg) + if m: + while m: + msg = m.group(1) + "0x" + m.group(3) + m = self.hexParser.match(msg) + l += " message=\"" + msg + "\"" + if self.condition != None: + l += " type=\"" + self.condition + "\"" + l += ">" + lines.append(l) + if len(self.output) > 0: + for o in self.output: + lines.append(o) + if len(self.filename) > 0: + lines.append(self.filename + ":<" + self.lineNumber + ">") + lines.append(" ") + return lines + +class TestConditionApprovedParser: + failedApprovedTestParser = re.compile( r'(.*\..pp).*:<(.*).*>:(.*FAILED)' ) + otherApprovedTestParser = re.compile( r'(.*\..pp).*:<(.*).*>:' ) + + def __init__(self): + self.current = TestConditionData() + + def parseApprovedLine(self,line): + result = None + if line.startswith("==============================================================================="): + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + + elif line.startswith("-------------------------------------------------------------------------------"): + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + + else: + if line.startswith("No assertions in"): + self.current.noAssertions = line.strip() + self.current.state = self.current.MESSAGES + else: + m = self.failedApprovedTestParser.match(line) + if m: + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + self.current.filename = m.group(1).strip() + self.current.lineNumber = m.group(2).strip() + self.current.reason = m.group(3).strip() + self.current.state = self.current.CONDITION + else: + m = self.otherApprovedTestParser.match(line) + if m: + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + self.current.filename = m.group(1).strip() + self.current.lineNumber = m.group(2).strip() + self.current.state = self.current.CONDITION + elif not(self.current.empty()): + self.current.parseCondition(line) + return result + +class TestConditionResultParser: + failedResultsTestParser = re.compile( r'(.*\\)(.*\..pp).*\((.*).*\):(.*FAILED)' ) + otherResultsTestParser = re.compile( r'(.*\\)(.*\..pp).*\((.*).*\):' ) + + def __init__(self): + self.current = TestConditionData() + + def parseResultLine(self,line): + result = None + if line.startswith("==============================================================================="): + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + + elif line.startswith("-------------------------------------------------------------------------------"): + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + + else: + if line.startswith("No assertions in"): + self.current.noAssertions = line.strip() + self.current.state = self.current.MESSAGES + else: + m = self.failedResultsTestParser.match(line) + if m: + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + self.current.filenamePrefix = m.group(1).strip() + self.current.filename = m.group(2).strip() + self.current.lineNumber = m.group(3).strip() + self.current.reason = m.group(4).strip() + self.current.state = self.current.CONDITION + else: + m = self.otherResultsTestParser.match(line) + if m: + if not(self.current.empty()): + result = self.current + self.current = TestConditionData() + self.current.filenamePrefix = m.group(1).strip() + self.current.filename = m.group(2).strip() + self.current.lineNumber = m.group(3).strip() + self.current.state = self.current.CONDITION + elif not(self.current.empty()): + self.current.parseCondition(line) + return result + diff --git a/scripts/catch_test_case.py b/scripts/catch_test_case.py new file mode 100644 index 00000000..b685c9e8 --- /dev/null +++ b/scripts/catch_test_case.py @@ -0,0 +1,584 @@ +import re + +from catch_conditions import TestConditionData +from catch_conditions import TestConditionApprovedParser +from catch_conditions import TestConditionResultParser + +class EndOfClassName: + @staticmethod + def parseRawLine(line): + if line.startswith("..............................................................................."): + return ParseResult.END_OF_CLASS_NAME + return ParseResult.NONE + +class TestCaseData: + + def __init__(self): + self.name = "" + self.nameParts = [] + self.classname = "global" + self.sections = [] + self.filenamePrefix = "" + self.filename = "" + self.lineNumber = "" + self.conditions = [] + self.sysout = [] + self.syserr = [] + self.result = "" + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __repr__(self): + result = "[" + self.name + ", [ " + suffix = "" + for section in self.sections: + result += suffix + result += repr(section) + suffix = ", " + result + " ] " + result += self.filename + ", " + self.lineNumber + " [ " + suffix = "" + for cond in self.conditions: + result += suffix + result += repr(cond) + suffix = ", " + result + " ] ]" + return result + + def empty(self): + if len(self.name): + return False + return True + + def generateApprovedLines(self): + if self.empty(): + raise Exception("Empty test case..." + repr(self)) + lines = [] + if len(self.name): + lines.append("-------------------------------------------------------------------------------") + for n in self.nameParts: + lines.append(n) + if len(self.sections) > 0: + for section in self.sections: + lines.append(" " + section) + lines.append("-------------------------------------------------------------------------------") + if len(self.filename): + lines.append(self.filename + ":<" + self.lineNumber + ">") + lines.append("...............................................................................") + lines.append("") + for cond in self.conditions: + lines += cond.generateApprovedLines() + return lines + + def generateResultLines(self): + if self.empty(): + raise Exception("Empty test case..." + repr(self)) + lines = [] + if len(self.name): + lines.append("-------------------------------------------------------------------------------") + for n in self.nameParts: + lines.append(n) + if len(self.sections) > 0: + for section in self.sections: + lines.append(" " + section) + lines.append("-------------------------------------------------------------------------------") + if len(self.filename): + lines.append(self.filenamePrefix + self.filename + "(" + self.lineNumber + ")") + lines.append("...............................................................................") + lines.append("") + for cond in self.conditions: + lines += cond.generateResultLines() + return lines + + def generateUnapprovedLines(self): + if self.empty(): + raise Exception("Empty test case..." + repr(self)) + lines = [] + if len(self.name): + lines.append("-------------------------------------------------------------------------------") + for n in self.nameParts: + lines.append(n) + if len(self.sections) > 0: + for section in self.sections: + lines.append(" " + section) + lines.append("-------------------------------------------------------------------------------") + if len(self.filename): + lines.append(self.filename + ":<" + "line number" + ">") + lines.append("...............................................................................") + lines.append("") + for cond in self.conditions: + lines += cond.generateUnapprovedLines() + return lines + + def generateUnapprovedJunit(self): + lines = [] + + condLines = [] + for cond in self.conditions: + condLines += cond.generateUnapprovedJunit() + + if len(self.name): + l = " 0 or len(self.sysout) > 0 or len(self.syserr) > 0: + l += ">" + else: + l += "/>" + lines.append(l) + #if len(self.sections) > 0: + # for section in self.sections: + # lines.append(" " + section) + #if len(self.filename): + # lines.append(self.filename + ":<" + "line number" + ">") + # lines.append("...............................................................................") + lines += condLines + if len(self.sysout) > 0: + lines.append(" ") + for l in self.sysout: + lines.append(l) + lines.append(" ") + if len(self.syserr) > 0: + lines.append(" ") + for l in self.syserr: + lines.append(l) + lines.append(" ") + if len(condLines) > 0 or len(self.sysout) > 0 or len(self.syserr) > 0: + lines.append(" ") + return lines + + def generateRecursiveSection(self, prefix, section): + lines = [] + #print "S:",section + if section[0] == "S": + l = " " + prefix + "
","") + li = li.replace("\"",""") + l += " description=\"" + li + "\"" + l += ">" + lines.append(l) + if len(section) > 4: + index = 4 + while index < len(section): + tmp = section[index] + if len(tmp) > 0: + if tmp[0] == "E": + l = " " + prefix + " 3: + cond = tmp[3] + if len(cond[0]) > 0: + lines.append(" " + prefix + "") + for li in cond[0]: + if len(li.strip()) > 0: + li = li.replace("<","<") + li = li.replace("<hex digits>","") + li = li.replace("\"",""") + lines.append(li) + lines.append(" " + prefix + "") + if len(cond[1]) > 0: + lines.append(" " + prefix + "") + for li in cond[1]: + if len(li.strip()) > 0: + li = li.replace("<","<") + li = li.replace("<hex digits>","") + li = li.replace("\"",""") + lines.append(li) + lines.append(" " + prefix + "") + if len(cond) > 2: + filename = cond[2] + lines.append(" " + prefix + "") + if len(cond) > 3: + tmp = cond[3] + for li in tmp: + if len(li.strip()) > 0: + lines.append(li) + lines.append(" " + prefix + "") + elif len(tmp) > 4: + print "RE:",tmp[4] + + l = " " + prefix + "" + lines.append(l) + elif tmp[0] == "X": + l = " " + prefix + "" + lines.append(l) + for li in tmp[2]: + if len(li.strip()) > 0: + lines.append(li) + l = " " + prefix + "" + lines.append(l) + elif tmp[0] == "F": + l = " " + prefix + "" + lines.append(l) + for li in tmp[1]: + if len(li.strip()) > 0: + lines.append(li) + l = " " + prefix + "" + lines.append(l) + elif tmp[0] == "S": + lines += self.generateRecursiveSection(prefix + " ", tmp) + else: + print "RS2:",tmp[0] + else: + print "RS:",section[index] + index += 1 + + lines.append(" " + prefix + "") + l = " " + prefix + "
" + lines.append(l) + return lines + + def generateSection(self, prefix, sections): + lines = [] + for section in sections: + #print "S:",section + if section[0] == "S": + lines += self.generateRecursiveSection(prefix, section) + elif section[0] == "E": + l = " " + prefix + " 3: + cond = section[3] + if len(cond[0]) > 0: + lines.append(" " + prefix + "") + for li in cond[0]: + if len(li.strip()) > 0: + li = li.replace("&","&") + li = li.replace("<","<") + li = li.replace("<hex digits>","") + li = li.replace("\"",""") + lines.append(li) + lines.append(" " + prefix + "") + if len(cond[1]) > 0: + lines.append(" " + prefix + "") + for li in cond[1]: + if len(li.strip()) > 0: + li = li.replace("<","<") + li = li.replace("<hex digits>","") + li = li.replace("\"",""") + lines.append(li) + lines.append(" " + prefix + "") + if len(cond) > 2: + filename = cond[2] + lines.append(" " + prefix + "") + if len(cond) > 3: + tmp = cond[3] + for li in tmp: + if len(li.strip()) > 0: + lines.append(li) + lines.append(" " + prefix + "") + elif len(section) > 4: + print "RE:",section[4] + + l = " " + prefix + "" + lines.append(l) + elif section[0] == "X": + l = " " + prefix + "" + lines.append(l) + for li in section[2]: + if len(li.strip()) > 0: + lines.append(li) + l = " " + prefix + "" + lines.append(l) + elif section[0] == "I": + l = " " + prefix + "" + lines.append(l) + for li in section[1]: + if len(li.strip()) > 0: + lines.append(li) + l = " " + prefix + "" + lines.append(l) + elif section[0] == "W": + l = " " + prefix + "" + lines.append(l) + for li in section[1]: + if len(li.strip()) > 0: + lines.append(li) + l = " " + prefix + "" + lines.append(l) + elif section[0] == "F": + l = " " + prefix + "" + lines.append(l) + for li in section[1]: + if len(li.strip()) > 0: + lines.append(li) + l = " " + prefix + "" + lines.append(l) + return lines + + def generateUnapprovedXml(self): + lines = [] + + if len(self.name): + l = " " + lines.append(l) + + if len(self.sections) > 0: + prefix = " " + lines += self.generateSection(prefix, self.sections) + + if len(self.result) > 0: + lines.append(" ") + + if len(self.name): + l = " " + lines.append(l) + return lines + + def addFailure(self, filename, lineNumber, output, message, type): + self.filename = filename + self.lineNumber = lineNumber + condition = TestConditionData() + condition.addFailure(filename, lineNumber, output, message, type) + self.conditions.append(condition) + + def addError(self, filename, lineNumber, output, message, type): + self.filename = filename + self.lineNumber = lineNumber + condition = TestConditionData() + condition.addError(filename, lineNumber, output, message, type) + self.conditions.append(condition) + + def addSysout(self, output): + self.sysout = output + + def addSyserr(self, output): + self.syserr = output + + def addOverallResult(self,r): + self.result = r + + def addSection(self,name,desc, results): + section = [] + section.append("S") + section.append(name) + section.append(desc) + section.append(results) + self.sections.append(section) + return section + + def addExpression(self,result,filename): + section = [] + section.append("E") + section.append(result) + section.append(filename) + self.sections.append(section) + return section + + def addException(self,filename,text): + section = [] + section.append("X") + section.append(filename) + section.append(text) + #print section + self.sections.append(section) + return section + + def addInfo(self,text): + section = [] + section.append("I") + section.append(text) + self.sections.append(section) + return section + + def addWarning(self,text): + section = [] + section.append("W") + section.append(text) + self.sections.append(section) + return section + + def addSimpleFailure(self,text): + section = [] + section.append("F") + section.append(text) + self.sections.append(section) + return section + + def addExpressionDetails(self, section, subExp): + section.append(subExp) + + def addSubException(self, section, filename, text): + tmp = [] + tmp.append("X") + tmp.append(filename) + tmp.append(text) + section.append(tmp) + + def addSubFailure(self, section, text): + tmp = [] + tmp.append("F") + tmp.append(text) + section.append(tmp) + + def addSubExpression(self,section,result,filename): + tmp = [] + tmp.append("E") + tmp.append(result) + tmp.append(filename) + section.append(tmp) + #print "Section:",section + return tmp + + def addSubSection(self,section,name,desc,results): + tmp = [] + tmp.append("S") + tmp.append(name) + tmp.append(desc) + tmp.append(results) + section.append(tmp) + #print "Section:",section + return tmp + +class TestCaseApprovedParser: + NONE = 0 + TEST_CASE_NAME_EXPECTED = 1 + TEST_CASE_NAME = 2 + TEST_CLASS_EXPECTED = 3 + END_OF_CLASS_NAME_EXPECTED = 4 + TEST_CONDITION_EXPECTED = 5 + + testFilenameParser = re.compile( r'(.*\..pp).*:<(.*).*>' ) + + def __init__(self): + self.state = self.NONE + self.current = TestCaseData() + self.conditionParser = TestConditionApprovedParser() + + def parseApprovedLine(self,line): + result = None + if self.state == self.NONE: + if line.startswith("-------------------------------------------------------------------------------"): + self.state = self.TEST_CASE_NAME_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CASE_NAME_EXPECTED: + if len(line): + self.current.name = line.strip() + self.current.nameParts.append(line.strip()) + self.state = self.TEST_CASE_NAME + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CASE_NAME: + if line.startswith("-------------------------------------------------------------------------------"): + self.state = self.TEST_CLASS_EXPECTED + elif line.startswith(" "): + #print "***SECTION: ",line + self.current.sections.append(line[2:]) + elif len(line): + if len(self.current.name) > 0: + self.current.name += line.strip() + else: + self.current.name = line.strip() + self.current.nameParts.append(line.strip()) + elif self.state == self.TEST_CLASS_EXPECTED: + m = self.testFilenameParser.match(line) + if m: + self.current.filename = m.group(1).strip() + self.current.lineNumber = m.group(2).strip() + self.state = self.END_OF_CLASS_NAME_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.END_OF_CLASS_NAME_EXPECTED: + if line.startswith("..............................................................................."): + self.state = self.TEST_CONDITION_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CONDITION_EXPECTED: + #print "**** LINE " + line + condition = self.conditionParser.parseApprovedLine(line) + if isinstance(condition, TestConditionData): + #print "**** CASE " + repr(condition) + self.current.conditions.append(condition) + if line.startswith("-------------------------------------------------------------------------------"): + result = self.current + self.current = TestCaseData() + self.state = self.TEST_CASE_NAME_EXPECTED + elif line.startswith("==============================================================================="): + result = self.current + self.current = TestCaseData() + self.state = self.NONE + + return result + +class TestCaseResultParser: + NONE = 0 + TEST_CASE_NAME_EXPECTED = 1 + TEST_CASE_NAME = 2 + TEST_CLASS_EXPECTED = 3 + END_OF_CLASS_NAME_EXPECTED = 4 + TEST_CONDITION_EXPECTED = 5 + + testFilenameParser = re.compile( r'(.*\\)(.*\..pp).*\((.*).*\)' ) + + def __init__(self): + self.state = self.NONE + self.current = TestCaseData() + self.conditionParser = TestConditionResultParser() + + def parseResultLine(self,line): + result = None + if self.state == self.NONE: + if line.startswith("-------------------------------------------------------------------------------"): + self.state = self.TEST_CASE_NAME_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CASE_NAME_EXPECTED: + if len(line): + self.current.name = line.strip() + self.current.nameParts.append(line.strip()) + self.state = self.TEST_CASE_NAME + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CASE_NAME: + if line.startswith("-------------------------------------------------------------------------------"): + self.state = self.TEST_CLASS_EXPECTED + elif line.startswith(" "): + #print "***SECTION: ",line + self.current.sections.append(line[2:]) + elif len(line): + if len(self.current.name) > 0: + self.current.name += line.strip() + else: + self.current.name = line.strip() + self.current.nameParts.append(line.strip()) + elif self.state == self.TEST_CLASS_EXPECTED: + m = self.testFilenameParser.match(line) + if m: + self.current.filenamePrefix = m.group(1).strip() + self.current.filename = m.group(2).strip() + self.current.lineNumber = m.group(3).strip() + self.state = self.END_OF_CLASS_NAME_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.END_OF_CLASS_NAME_EXPECTED: + if line.startswith("..............................................................................."): + self.state = self.TEST_CONDITION_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CONDITION_EXPECTED: + #print "**** LINE " + line + condition = self.conditionParser.parseResultLine(line) + if isinstance(condition, TestConditionData): + #print "**** CASE " + repr(condition) + self.current.conditions.append(condition) + if line.startswith("-------------------------------------------------------------------------------"): + result = self.current + self.current = TestCaseData() + self.state = self.TEST_CASE_NAME_EXPECTED + elif line.startswith("==============================================================================="): + result = self.current + self.current = TestCaseData() + self.state = self.NONE + + return result + diff --git a/scripts/catch_test_run.py b/scripts/catch_test_run.py new file mode 100644 index 00000000..833d68a0 --- /dev/null +++ b/scripts/catch_test_run.py @@ -0,0 +1,430 @@ +import re +import os + +from catch_test_case import TestCaseApprovedParser +from catch_test_case import TestCaseResultParser +from catch_test_case import TestCaseData +from catch_conditions import RandomOutput + +class TestRunData: + + def __init__(self): + self.appname = "" + self.version = "" + self.testcases = [] + self.results = "" + self.output = [] + self.outputLine = 0 + self.writtenOutput = False + self.sysout = [] + self.syserr = [] + self.errors = "" + self.failures = "" + self.tests = "" + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __repr__(self): + result = "[" + self.appname + ", " + self.version + " [ " + suffix = "" + for case in self.testcases: + result += suffix + result += repr(case) + suffix = ", " + result += " ]" + result += self.results + result += " ]" + return result + + def empty(self): + if len(self.appname): + return False + return True + + def generateApprovedLines(self): + if self.empty(): + raise Exception("Empty test run..." + repr(self)) + lines = [] + self.writtenOutput = False + if not(self.writtenOutput) and len(self.output) > 0 and self.outputLine == 0: + lines += self.output + self.writtenOutput = True + if len(self.appname): + lines.append("") + lines.append("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + lines.append(self.appname + " is a " + self.version + " host application.") + lines.append("Run with -? for options") + lines.append("") + for case in self.testcases: + lines += case.generateApprovedLines() + if not(self.writtenOutput) and len(self.output) > 0 and len(lines) >= self.outputLine: + lines += self.output + self.writtenOutput = True + lines.append("===============================================================================") + lines.append(self.results) + lines.append("") + + return lines + + def generateSortedApprovedLines(self): + if self.empty(): + raise Exception("Empty test run..." + repr(self)) + lines = [] + self.writtenOutput = False + if not(self.writtenOutput) and len(self.output) > 0 and self.outputLine == 0: + lines += self.output + self.writtenOutput = True + if len(self.appname): + lines.append("") + lines.append("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + lines.append(self.appname + " is a " + self.version + " host application.") + lines.append("Run with -? for options") + lines.append("") + sortedTestcases = sorted(self.testcases, key=lambda x: x.name, reverse=False) + for case in sortedTestcases: + lines += case.generateApprovedLines() + if not(self.writtenOutput) and len(self.output) > 0 and len(lines) >= self.outputLine: + lines += self.output + self.writtenOutput = True + lines.append("===============================================================================") + lines.append(self.results) + lines.append("") + + return lines + + def generateResultLines(self): + if self.empty(): + raise Exception("Empty test run..." + repr(self)) + lines = [] + self.writtenOutput = False + if not(self.writtenOutput) and len(self.output) > 0 and self.outputLine == 0: + lines += self.output + self.writtenOutput = True + if len(self.appname): + lines.append("") + lines.append("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + lines.append(self.appname + " is a " + self.version + " host application.") + lines.append("Run with -? for options") + lines.append("") + for case in self.testcases: + lines += case.generateResultLines() + if not(self.writtenOutput) and len(self.output) > 0 and len(lines) >= self.outputLine: + lines += self.output + self.writtenOutput = True + lines.append("===============================================================================") + lines.append(self.results) + lines.append("") + + return lines + + def generateUnapprovedLines(self, outputLine): + if self.empty(): + raise Exception("Empty test run..." + repr(self)) + lines = [] + self.writtenOutput = False + #print "U:",outputLine,",",self.output + if not(self.writtenOutput) and len(self.output) > 0 and outputLine == 0: + lines += self.output + self.writtenOutput = True + if len(self.appname): + lines.append("") + lines.append("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + lines.append("CatchSelfTest" + " is a " + "" + " host application.") + lines.append("Run with -? for options") + lines.append("") + for case in self.testcases: + lines += case.generateUnapprovedLines() + if not(self.writtenOutput) and len(self.output) > 0 and len(lines) >= outputLine: + lines += self.output + self.writtenOutput = True + lines.append("===============================================================================") + lines.append(self.results) + lines.append("") + + return lines + + def generateSortedUnapprovedLines(self, outputLine): + if self.empty(): + raise Exception("Empty test run..." + repr(self)) + lines = [] + self.writtenOutput = False + #print "U:",outputLine,",",self.output + if not(self.writtenOutput) and len(self.output) > 0 and outputLine == 0: + lines += self.output + self.writtenOutput = True + if len(self.appname): + lines.append("") + lines.append("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + lines.append("CatchSelfTest" + " is a " + "" + " host application.") + lines.append("Run with -? for options") + lines.append("") + sortedTestcases = sorted(self.testcases, key=lambda x: x.name, reverse=False) + for case in sortedTestcases: + lines += case.generateUnapprovedLines() + if not(self.writtenOutput) and len(self.output) > 0 and len(lines) >= outputLine: + lines += self.output + self.writtenOutput = True + lines.append("===============================================================================") + lines.append(self.results) + lines.append("") + + return lines + + def generateSortedUnapprovedJunit(self): + lines = [] + #print "U:",outputLine,",",self.output + lines.append("") + l = " " + lines.append(l) + sortedTestcases = sorted(self.testcases, key=lambda x: x.classname, reverse=False) + sortedTestcases = sorted(sortedTestcases, key=lambda x: x.name, reverse=False) + #sortedTestcases = self.testcases + for case in sortedTestcases: + lines += case.generateUnapprovedJunit() + + if len(self.sysout) > 0: + lines.append(" ") + for l in self.sysout: + lines.append(l) + lines.append(" ") + if len(self.syserr) > 0: + lines.append(" ") + for l in self.syserr: + lines.append(l) + lines.append(" ") + + lines.append(" ") + lines.append("") + return lines + + def generateSortedUnapprovedXml(self): + lines = [] + #print "U:",outputLine,",",self.output + lines.append("") + lines.append(" ") + + sortedTestcases = sorted(self.testcases, key=lambda x: x.classname, reverse=False) + sortedTestcases = sorted(sortedTestcases, key=lambda x: x.name, reverse=False) + #sortedTestcases = self.testcases + for case in sortedTestcases: + lines += case.generateUnapprovedXml() + + l = "" + lines.append(" " + l) + lines.append(" ") + lines.append(" " + l) + lines.append("") + return lines + + def addTestCase(self, name): + testcase = TestCaseData() + testcase.name = name + testcase.nameParts.append(name) + self.testcases.append(testcase) + return testcase + + def addClassTestCase(self, cls, name): + testcase = TestCaseData() + testcase.classname = cls + testcase.name = name + testcase.nameParts.append(name) + self.testcases.append(testcase) + return testcase + + def addSysout(self, output): + self.sysout = output + + def addSyserr(self, output): + self.syserr = output + +class TestRunApprovedParser: + NONE = 0 + VERSION_EXPECTED = 1 + TEST_CASE_EXPECTED = 2 + END_RUN_INFO = 3 + + versionParser = re.compile( r'(.*)is a (*).*' ) + + def __init__(self): + self.state = self.NONE + self.current = TestRunData() + self.testcaseParser = TestCaseApprovedParser() + self.lineNumber = 0 + + def parseApprovedLine(self,line): + result = None + if self.state == self.NONE: + if line.startswith("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"): + self.state = self.VERSION_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.VERSION_EXPECTED: + m = self.versionParser.match(line) + if m: + self.current.appname = m.group(1).strip() + self.current.version = m.group(2).strip() + self.state = self.TEST_CASE_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CASE_EXPECTED: + if line == "Run with -? for options": + pass + else: + testcase = None + try: + testcase = self.testcaseParser.parseApprovedLine(line) + except RandomOutput as e: + #print "E:", self.lineNumber, ", ",e.output + self.current.output = e.output + self.current.outputLine = self.lineNumber - 10 + + if isinstance(testcase, TestCaseData): + self.current.testcases.append(testcase) + if line.startswith("==============================================================================="): + self.state = self.END_RUN_INFO + elif self.state == self.END_RUN_INFO: + if len(line): + self.current.results = line.strip() + result = self.current + + self.lineNumber += 1 + return result + +class TestRunApprovedHandler: + + def __init__(self, filePath): + rawFile = open( filePath, 'r' ) + parser = TestRunApprovedParser() + lineNumber = 0 + self.current = None + for line in rawFile: + line = line.rstrip() + #print "L:", lineNumber, "'",line,"'" + result = parser.parseApprovedLine(line) + if isinstance(result, TestRunData): + self.current = result + lineNumber += 1 + if not(isinstance(self.current, TestRunData) ): + raise Exception("File could not be parsed: '" + filePath + "'") + + def writeRawFile(self,filePath): + rawWriteFile = open( filePath, 'wb' ) + lines = self.current.generateApprovedLines() + for line in lines: + rawWriteFile.write(line + "\n") + + def writeSortedRawFile(self,filePath): + rawWriteFile = open( filePath, 'wb' ) + lines = self.current.generateSortedApprovedLines() + for line in lines: + rawWriteFile.write(line + "\n") + +class TestRunResultParser: + NONE = 0 + VERSION_EXPECTED = 1 + TEST_CASE_EXPECTED = 2 + END_RUN_INFO = 3 + + versionParser = re.compile( r'(.*)is a (Catch v[0-9]*.[0-9]* b[0-9]*).*' ) + + def __init__(self): + self.state = self.NONE + self.current = TestRunData() + self.testcaseParser = TestCaseResultParser() + self.lineNumber = 0 + + def parseResultLine(self,line): + result = None + if self.state == self.NONE: + if line.startswith("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"): + self.state = self.VERSION_EXPECTED + elif len(line): + self.current.output.append(line.strip()) + if len(self.current.output) == 10: + if (self.current.output[0] == "Message from section one" and self.current.output[1] == "Message from section two" and + self.current.output[2] == "Some information" and self.current.output[3] == "An error" and + self.current.output[4] == "Message from section one" and self.current.output[5] == "Message from section two" and + self.current.output[6] == "Some information" and self.current.output[7] == "An error" and + self.current.output[8] == "hello" and self.current.output[9] == "hello" ): + + self.current.outputLine = self.lineNumber - 9 + + elif self.state == self.VERSION_EXPECTED: + m = self.versionParser.match(line) + if m: + self.current.appname = m.group(1).strip() + self.current.version = m.group(2).strip() + self.state = self.TEST_CASE_EXPECTED + elif len(line): + raise Exception("Unknown parse line: '" + line + "'") + elif self.state == self.TEST_CASE_EXPECTED: + if line == "Run with -? for options": + pass + else: + testcase = None + try: + testcase = self.testcaseParser.parseResultLine(line) + except RandomOutput as e: + #print "E:", self.lineNumber, ", ",e.output + self.current.output = e.output + self.current.outputLine = self.lineNumber - 10 + + if isinstance(testcase, TestCaseData): + self.current.testcases.append(testcase) + if line.startswith("==============================================================================="): + self.state = self.END_RUN_INFO + elif self.state == self.END_RUN_INFO: + if len(line): + self.current.results = line.strip() + result = self.current + + self.lineNumber += 1 + return result + +class TestRunResultHandler: + + def __init__(self, filePath): + rawFile = open( filePath, 'r' ) + parser = TestRunResultParser() + lineNumber = 0 + self.current = None + for line in rawFile: + line = line.rstrip() + #print "L:", lineNumber, "'",line,"'" + result = parser.parseResultLine(line) + if isinstance(result, TestRunData): + self.current = result + lineNumber += 1 + if not(isinstance(self.current, TestRunData) ): + raise Exception("File could not be parsed: '" + filePath + "'") + + def writeRawFile(self,filePath): + rawWriteFile = open( filePath, 'wb' ) + lines = self.current.generateResultLines() + for line in lines: + rawWriteFile.write(line + os.linesep) + + def writeUnapprovedFile(self,filePath,outputLine): + rawWriteFile = open( filePath, 'wb' ) + lines = self.current.generateUnapprovedLines(outputLine) + for line in lines: + rawWriteFile.write(line + "\n") + def writeSortedUnapprovedFile(self,filePath,outputLine): + rawWriteFile = open( filePath, 'wb' ) + lines = self.current.generateSortedUnapprovedLines(outputLine) + for line in lines: + rawWriteFile.write(line + "\n") diff --git a/scripts/test_conditions.py b/scripts/test_conditions.py new file mode 100644 index 00000000..2b11bf7b --- /dev/null +++ b/scripts/test_conditions.py @@ -0,0 +1,698 @@ +import unittest +import catch_conditions + +from catch_conditions import TestConditionApprovedParser +from catch_conditions import TestConditionResultParser +from catch_conditions import TestConditionData +from catch_conditions import RandomOutput + +class ConditionTest(unittest.TestCase): + + def testConditionEquality(self): + c1 = TestConditionData() + c2 = TestConditionData() + c1.state = TestConditionData.CONDITION + c2.state = TestConditionData.EXPANSION + c1.filenamePrefix = "..\\..\\Test" + c2.filenamePrefix = "..\\..\\Test" + self.assertTrue(c1 == c2) + c2.filenamePrefix = "..\\..\\Junk" + self.assertFalse(c1 == c2) + self.assertTrue(c1 != c2) + + def testEndOfTestRunIsFound(self): + obj = TestConditionApprovedParser() + line = "===============================================================================" + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(obj.current.empty()) + + def testEndOfTestCaseIsFound(self): + obj = TestConditionApprovedParser() + line = "-------------------------------------------------------------------------------" + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(obj.current.empty()) + + def testFailedConditionIsFound(self): + obj = TestConditionApprovedParser() + line = "ClassTests.cpp:: FAILED:" + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue( not(obj.current.empty()) ) + + def testOtherConditionIsFound(self): + obj = TestConditionApprovedParser() + line = "ClassTests.cpp::" + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue( not(obj.current.empty()) ) + + def testFailedConditionSetsReason(self): + obj = TestConditionApprovedParser() + line = "ClassTests.cpp:: FAILED:" + result = obj.parseApprovedLine(line) + self.assertTrue(obj.current.reason == "FAILED") + self.assertTrue(obj.current.filename == "ClassTests.cpp") + self.assertTrue(obj.current.lineNumber == "line number") + + def testOtherConditionSetsFileNameAndLine(self): + obj = TestConditionApprovedParser() + line = "MessageTests.cpp::" + result = obj.parseApprovedLine(line) + self.assertTrue(obj.current.filename == "MessageTests.cpp") + self.assertTrue(obj.current.lineNumber == "line number") + + def testFailedConditionSetsCondition(self): + obj = TestConditionApprovedParser() + lines = ["ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + ""] + for line in lines: + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + + self.assertTrue(obj.current.condition == "REQUIRE( s == \"world\" )") + newLines = obj.current.generateApprovedLines() + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testExpansionConditionReturnsExpansion(self): + obj = TestConditionApprovedParser() + lines = ["ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:" ] + for line in lines: + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + + def testExpansionSetsExpansion(self): + obj = TestConditionApprovedParser() + lines = [ "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " 1 == 2", + "", + "-------------------------------------------------------------------------------" + ] + for line in lines: + result = obj.parseApprovedLine(line) + #print lines + self.assertTrue(isinstance(result, TestConditionData)) + self.assertTrue(len(result.expansion) == 1) + self.assertTrue(result.expansion[0] == "1 == 2") + newLines = result.generateApprovedLines() + newLines.append("-------------------------------------------------------------------------------") + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testTwoConditions(self): + obj = TestConditionApprovedParser() + lines = [ "ConditionTests.cpp:: FAILED:", + " CHECK( data.int_seven == 6 )", + "with expansion:", + " 7 == 6", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.int_seven == 8 )", + "with expansion:", + " 7 == 8", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testSuccessConditions(self): + obj = TestConditionApprovedParser() + lines = [ "ApproxTests.cpp::", + "PASSED:", + " REQUIRE( d == Approx( 1.23 ) )", + "with expansion:", + " 1.23 == Approx( 1.23 )", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print result + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testConditionsWithoutExpansion(self): + obj = TestConditionApprovedParser() + lines = [ "ConditionTests.cpp:: FAILED:", + " CHECK( false != false )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( true != true )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( !true )", + "with expansion:", + " false", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testExceptionsExplicit(self): + obj = TestConditionApprovedParser() + lines = [ "ExceptionTests.cpp:: FAILED:", + " CHECK_THROWS_AS( thisThrows() )", + "due to unexpected exception with message:", + " expected exception", + "", + "ExceptionTests.cpp:: FAILED:", + " CHECK_THROWS_AS( thisDoesntThrow() )", + "because no exception was thrown where one was expected:", + "", + "ExceptionTests.cpp:: FAILED:", + " CHECK_NOTHROW( thisThrows() )", + "due to unexpected exception with message:", + " expected exception", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testExceptionsImplicit(self): + obj = TestConditionApprovedParser() + lines = [ "ExceptionTests.cpp:: FAILED:", + "due to unexpected exception with message:", + " unexpected exception", + "", + "ExceptionTests.cpp:: FAILED:", + " {Unknown expression after the reported line}", + "due to unexpected exception with message:", + " unexpected exception", + "", + "ExceptionTests.cpp:: FAILED:", + "due to unexpected exception with message:", + " 3.14", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testWarning(self): + obj = TestConditionApprovedParser() + lines = [ "MessageTests.cpp::", + "warning:", + " this is a warning", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMessages1(self): + obj = TestConditionApprovedParser() + lines = [ "MessageTests.cpp:: FAILED:", + " REQUIRE( a == 1 )", + "with expansion:", + " 2 == 1", + "with messages:", + " this message should be logged", + " so should this", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMessagesExplicitFail(self): + obj = TestConditionApprovedParser() + lines = [ "MessageTests.cpp:: FAILED:", + "explicitly with message:", + " This is a failure", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMessagesOutput(self): + obj = TestConditionApprovedParser() + lines = [ "MessageTests.cpp:: FAILED:", + "explicitly with message:", + " Message from section two", + "", + "Message from section one", + "Message from section two", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMultiMessages(self): + obj = TestConditionApprovedParser() + lines = [ "MessageTests.cpp:: FAILED:", + " REQUIRE( i < 10 )", + "with expansion:", + " 10 < 10", + "with messages:", + " current counter 10", + " i := 10", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMiscMessages(self): + obj = TestConditionApprovedParser() + lines = [ "MiscTests.cpp:: FAILED:", + " CHECK( ( fib[i] % 2 ) == 0 )", + "with expansion:", + " 1 == 0", + "with message:", + " Testing if fib[0] (1) is even", + "", + "MiscTests.cpp:: FAILED:", + " CHECK( ( fib[i] % 2 ) == 0 )", + "with expansion:", + " 1 == 0", + "with message:", + " Testing if fib[1] (1) is even", + "", + "MiscTests.cpp:: FAILED:", + " CHECK( ( fib[i] % 2 ) == 0 )", + "with expansion:", + " 1 == 0", + "with message:", + " Testing if fib[3] (3) is even", + "", + "MiscTests.cpp:: FAILED:", + " CHECK( ( fib[i] % 2 ) == 0 )", + "with expansion:", + " 1 == 0", + "with message:", + " Testing if fib[4] (5) is even", + "", + "MiscTests.cpp:: FAILED:", + " CHECK( ( fib[i] % 2 ) == 0 )", + "with expansion:", + " 1 == 0", + "with message:", + " Testing if fib[6] (13) is even", + "", + "MiscTests.cpp:: FAILED:", + " CHECK( ( fib[i] % 2 ) == 0 )", + "with expansion:", + " 1 == 0", + "with message:", + " Testing if fib[7] (21) is even", + "", + "Some information", + "An error", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testRandomOutput(self): + obj = TestConditionApprovedParser() + lines = [ "MiscTests.cpp:: FAILED:", + "explicitly with message:", + " to infinity and beyond", + "", + "Message from section one", + "Message from section two", + "Some information", + "An error", + "Message from section one", + "Message from section two", + "Some information", + "An error", + "hello", + "hello", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + try: + result = obj.parseApprovedLine(line) + except RandomOutput as e: + randomOutput = e.output + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + self.assertTrue( len(randomOutput) == 10) + newLines += randomOutput + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMultiLineWarning(self): + obj = TestConditionApprovedParser() + lines = [ "TrickyTests.cpp::", + "warning:", + " Uncomment the code in this test to check that it gives a sensible compiler", + " error", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMultiMessagesAfterCondition(self): + obj = TestConditionApprovedParser() + lines = [ "MiscTests.cpp:: FAILED:", + " REQUIRE( false )", + "with messages:", + " hi", + " i := 7", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + randomOutput = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testNoAssertions(self): + obj = TestConditionApprovedParser() + lines = [ "", + "No assertions in test case './succeeding/exceptions/implicit'", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + randomOutput = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testNoAssertionsWithOutput(self): + obj = TestConditionApprovedParser() + lines = [ "", + "No assertions in section 'one'", + "", + "Message from section two", + "-------------------------------------------------------------------------------" + ] + newLines = [] + randomOutput = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testFailedButOk(self): + obj = TestConditionApprovedParser() + lines = [ "MessageTests.cpp::", + "FAILED - but was ok:", + " CHECK_NOFAIL( 1 == 2 )", + "", + "", + "No assertions in test case './succeeding/nofail'", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + randomOutput = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMultiLineExpansion(self): + obj = TestConditionApprovedParser() + lines = [ "MiscTests.cpp::", + "PASSED:", + " CHECK_THAT( testStringForMatching() AllOf( Catch::Contains( \"string\" ), Catch::Contains( \"abc\" ) ) )", + "with expansion:", + " \"this string contains 'abc' as a substring\" ( contains: \"string\" and", + " contains: \"abc\" )", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + randomOutput = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMultiLineExpansionWithWrap(self): + obj = TestConditionApprovedParser() + lines = [ "TestMain.cpp::", + "PASSED:", + " CHECK( text.toString() == \" one two\n three\n four\" )", + "with expansion:", + " \" one two", + " three", + " four\"", + " ==", + " \" one two", + " three", + " four\"", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + randomOutput = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testMultiLineExpansionWithTruncation(self): + obj = TestConditionApprovedParser() + lines = [ "TestMain.cpp::", + "PASSED:", + " CHECK_THAT( t.toString() EndsWith( \"... message truncated due to excessive size\" ) )", + "with expansion:", + " \"***************************************************************************-", + " ***-", + " ****************************************************************************-", + " **-", + "... message truncated due to excessive size", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateApprovedLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testBasicResultsParser(self): + obj = TestConditionResultParser() + lines = [ "..\..\..\SelfTest\ClassTests.cpp(28): FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseResultLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateResultLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testBasicResultsWarning(self): + obj = TestConditionResultParser() + lines = [ "..\..\..\SelfTest\MessageTests.cpp(17): ", + "warning:", + " this is a warning", + "", + "-------------------------------------------------------------------------------" + ] + newLines = [] + for line in lines: + result = obj.parseResultLine(line) + if isinstance(result, TestConditionData): + #print result + newLines += result.generateResultLines() + + newLines.append("-------------------------------------------------------------------------------") + #print lines + #print newLines + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/scripts/test_test_case.py b/scripts/test_test_case.py new file mode 100644 index 00000000..6976453c --- /dev/null +++ b/scripts/test_test_case.py @@ -0,0 +1,341 @@ +import unittest + +from catch_test_case import TestCaseApprovedParser +from catch_test_case import TestCaseResultParser +from catch_test_case import TestCaseData +from catch_conditions import TestConditionData + +class TestCaseTest(unittest.TestCase): + + def testTestCaseEquality(self): + c1 = TestConditionData() + c2 = TestConditionData() + c1.filenamePrefix = "..\\..\\Test" + c2.filenamePrefix = "..\\..\\Junk" + t1 = TestCaseData() + t2 = TestCaseData() + t1.name = "Test 1" + t2.name = "Test 1" + t1.conditions.append(c1) + t1.conditions.append(c2) + t2.conditions.append(c1) + t2.conditions.append(c2) + self.assertTrue(t1 == t2) + + c3 = TestConditionData() + c3.filenamePrefix = "..\\..\\Fail" + t2.conditions.append(c3) + self.assertFalse(t1 == t2) + + t1.conditions.append(c3) + self.assertTrue(t1 == t2) + + t2.name = "Test 2" + self.assertFalse(t1 == t2) + + def testEndOfTestCaseIsFound(self): + obj = TestCaseApprovedParser() + line = "-------------------------------------------------------------------------------" + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(obj.current.empty()) + + def testTestCaseNameIsFound(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------" ] + for line in lines: + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(obj.current.name == "./failing/TestClass/failingCase") + self.assertTrue( not(obj.current.empty()) ) + + def testTestCaseClassIsFound(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "..............................................................................." ] + for line in lines: + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(obj.current.filename == "ClassTests.cpp") + self.assertTrue(obj.current.lineNumber == "line number") + + def testPartialConditionRequiresMoreData(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "ClassTests.cpp:: FAILED:" ] + for line in lines: + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(len(obj.current.conditions) == 0) + + def testTestCaseConditionIsFound(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\""] + for line in lines: + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + self.assertTrue(not(obj.conditionParser.current.empty())) + self.assertTrue(obj.conditionParser.current.reason == "FAILED") + + def testTestCaseConditionIsInsertedIntoList(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "-------------------------------------------------------------------------------"] + for line in lines: + result = obj.parseApprovedLine(line) + + self.assertTrue(isinstance(result, TestCaseData)) + self.assertTrue(len(result.conditions) > 0) + self.assertTrue(result.conditions[0].filename == "ClassTests.cpp" ) + self.assertTrue(result.conditions[0].lineNumber == "line number" ) + self.assertTrue(result.conditions[0].reason == "FAILED" ) + newLines = result.generateApprovedLines() + newLines.append("-------------------------------------------------------------------------------") + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testTwoTestCases(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "-------------------------------------------------------------------------------", + "./failing/Fixture/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( m_a == 2 )", + "with expansion:", + " 1 == 2", + "", + "===============================================================================" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestCaseData): + newLines += result.generateApprovedLines() + + newLines.append("===============================================================================") + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testTestCaseMultiConditionMatches(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/conditions/equality", + "-------------------------------------------------------------------------------", + "ConditionTests.cpp:", + "...............................................................................", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.int_seven == 6 )", + "with expansion:", + " 7 == 6", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.int_seven == 8 )", + "with expansion:", + " 7 == 8", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.int_seven == 0 )", + "with expansion:", + " 7 == 0", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.float_nine_point_one == Approx( 9.11f ) )", + "with expansion:", + " 9.1 == Approx( 9.11 )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.float_nine_point_one == Approx( 9.0f ) )", + "with expansion:", + " 9.1 == Approx( 9 )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.float_nine_point_one == Approx( 1 ) )", + "with expansion:", + " 9.1 == Approx( 1 )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.float_nine_point_one == Approx( 0 ) )", + "with expansion:", + " 9.1 == Approx( 0 )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.double_pi == Approx( 3.1415 ) )", + "with expansion:", + " 3.1415926535 == Approx( 3.1415 )", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.str_hello == \"goodbye\" )", + "with expansion:", + " \"hello\" == \"goodbye\"", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.str_hello == \"hell\" )", + "with expansion:", + " \"hello\" == \"hell\"", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.str_hello == \"hello1\" )", + "with expansion:", + " \"hello\" == \"hello1\"", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( data.str_hello.size() == 6 )", + "with expansion:", + " 5 == 6", + "", + "ConditionTests.cpp:: FAILED:", + " CHECK( x == Approx( 1.301 ) )", + "with expansion:", + " 1.3 == Approx( 1.301 )", + "", + "===============================================================================" + ] + + for line in lines: + result = obj.parseApprovedLine(line) + + self.assertTrue(isinstance(result, TestCaseData)) + newLines = result.generateApprovedLines() + newLines.append("===============================================================================") + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testOneSection(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/message/sections", + " one", + "-------------------------------------------------------------------------------", + "MessageTests.cpp:", + "...............................................................................", + "", + "MessageTests.cpp:: FAILED:", + "explicitly with message:", + " Message from section one", + "", + "===============================================================================" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestCaseData): + newLines += result.generateApprovedLines() + + newLines.append("===============================================================================") + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testOneSection(self): + obj = TestCaseApprovedParser() + lines = [ "-------------------------------------------------------------------------------", + "Comparisons between unsigned ints and negative signed ints match c++ standard", + "behaviour", + "-------------------------------------------------------------------------------", + "ConditionTests.cpp:", + "...............................................................................", + "", + "ConditionTests.cpp::", + "PASSED:", + " CHECK( ( -1 > 2u ) )", + "with expansion:", + " true", + "", + "ConditionTests.cpp::", + "PASSED:", + " CHECK( -1 > 2u )", + "with expansion:", + " -1 > 2", + "", + "===============================================================================" + ] + newLines = [] + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestCaseData): + newLines += result.generateApprovedLines() + + newLines.append("===============================================================================") + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testCaseBasicResults(self): + obj = TestCaseResultParser() + lines = [ "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "..\..\..\SelfTest\ClassTests.cpp(34)", + "...............................................................................", + "", + "..\..\..\SelfTest\ClassTests.cpp(28): FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "===============================================================================" + ] + + newLines = [] + for line in lines: + result = obj.parseResultLine(line) + if isinstance(result, TestCaseData): + newLines += result.generateResultLines() + + newLines.append("===============================================================================") + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/scripts/test_test_run.py b/scripts/test_test_run.py new file mode 100644 index 00000000..c75c35e4 --- /dev/null +++ b/scripts/test_test_run.py @@ -0,0 +1,334 @@ +import unittest +import catch_test_case + +from catch_test_run import TestRunData +from catch_test_run import TestRunApprovedParser +from catch_test_run import TestRunResultParser +from catch_test_case import TestCaseData +from catch_conditions import TestConditionData + +class TestCaseTest(unittest.TestCase): + + def testTestRunEquality(self): + c1 = TestConditionData() + c2 = TestConditionData() + c1.filenamePrefix = "..\\..\\Test" + c2.filenamePrefix = "..\\..\\Junk" + t1 = TestCaseData() + t2 = TestCaseData() + t1.name = "Test 1" + t2.name = "Test 1" + t1.conditions.append(c1) + t1.conditions.append(c2) + t2.conditions.append(c1) + t2.conditions.append(c2) + r1 = TestRunData() + r2 = TestRunData() + r1.appname = "One" + r2.appname = "One" + self.assertTrue(r1 == r2) + + r1.testcases.append(t1) + self.assertFalse(r1 == r2) + + r2.testcases.append(t2) + self.assertTrue(r1 == r2) + + c3 = TestConditionData() + c3.filenamePrefix = "..\\..\\Fail" + t2.conditions.append(c3) + self.assertFalse(r1 == r2) + + def testStartOfTestRunIsFound(self): + obj = TestRunApprovedParser() + line = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + result = obj.parseApprovedLine(line) + self.assertTrue(result == None) + + def testTestRunVersionIsSet(self): + obj = TestRunApprovedParser() + lines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options" ] + for line in lines: + result = obj.parseApprovedLine(line) + #print obj + self.assertTrue(result == None) + self.assertTrue(obj.current.appname == "CatchSelfTest") + self.assertTrue(obj.current.version == "") + + def testTestRunParsesTestCase(self): + obj = TestRunApprovedParser() + lines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:" + " \"hello\" == \"world\""] + for line in lines: + result = obj.parseApprovedLine(line) + #print obj + self.assertTrue(obj.testcaseParser.conditionParser.current.filename == "ClassTests.cpp" ) + self.assertTrue(obj.testcaseParser.conditionParser.current.lineNumber == "line number" ) + self.assertTrue(obj.testcaseParser.conditionParser.current.reason == "FAILED" ) + + def testTestRunAddsTestCase(self): + obj = TestRunApprovedParser() + lines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:" + " \"hello\" == \"world\"", + "", + "-------------------------------------------------------------------------------" + ] + for line in lines: + result = obj.parseApprovedLine(line) + #print obj + self.assertTrue( result == None ) + self.assertTrue( len(obj.current.testcases) == 1 ) + self.assertTrue(obj.current.testcases[0].filename == "ClassTests.cpp" ) + self.assertTrue(obj.current.testcases[0].lineNumber == "line number" ) + self.assertTrue( len(obj.current.testcases[0].conditions) == 1 ) + self.assertTrue(obj.current.testcases[0].conditions[0].filename == "ClassTests.cpp" ) + self.assertTrue(obj.current.testcases[0].conditions[0].lineNumber == "line number" ) + self.assertTrue(obj.current.testcases[0].conditions[0].reason == "FAILED" ) + + def testTestRunParsesTwoTestCases(self): + obj = TestRunApprovedParser() + lines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options", + "", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "-------------------------------------------------------------------------------", + "./failing/Fixture/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( m_a == 2 )", + "with expansion:", + " 1 == 2", + "", + "===============================================================================", + "122 test cases - 35 failed (753 assertions - 90 failed)", + "" + ] + testRun = None + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestRunData): + testRun = result + + self.assertTrue( isinstance(testRun, TestRunData) ) + newLines = testRun.generateApprovedLines() + #for line in newLines: + # print "L:",line + #print len(lines),",",len(newLines) + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testTestRunWithRandomOutput(self): + obj = TestRunApprovedParser() + lines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options", + "", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "MiscTests.cpp:: FAILED:", + "explicitly with message:", + " to infinity and beyond", + "", + "Message from section one", + "Message from section two", + "Some information", + "An error", + "Message from section one", + "Message from section two", + "Some information", + "An error", + "hello", + "hello", + "===============================================================================", + "122 test cases - 35 failed (753 assertions - 90 failed)", + "" + ] + testRun = None + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestRunData): + testRun = result + + self.assertTrue( isinstance(testRun, TestRunData) ) + #print "O:",result.outputLine + self.assertTrue( testRun.outputLine == 14 ) + newLines = testRun.generateApprovedLines() + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testRunBasicResult(self): + obj = TestRunResultParser() + lines = [ "Message from section one", + "Message from section two", + "Some information", + "An error", + "Message from section one", + "Message from section two", + "Some information", + "An error", + "hello", + "hello", + "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "TestCatch.exe is a Catch v1.0 b13 host application.", + "Run with -? for options", + "", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "..\..\..\SelfTest\ClassTests.cpp(34)", + "...............................................................................", + "", + "..\..\..\SelfTest\ClassTests.cpp(28): FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "===============================================================================", + "122 test cases - 35 failed (753 assertions - 90 failed)", + "" + ] + + testRun = None + for line in lines: + result = obj.parseResultLine(line) + if isinstance(result, TestRunData): + testRun = result + + self.assertTrue( isinstance(testRun, TestRunData) ) + #print "O:",testRun.outputLine + self.assertTrue( testRun.outputLine == 0 ) + newLines = testRun.generateResultLines() + #for line in newLines: + # print line + self.assertTrue( len(lines) == len(newLines) ) + self.assertTrue( lines == newLines ) + + def testTestRunSorted(self): + obj = TestRunApprovedParser() + lines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options", + "", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "-------------------------------------------------------------------------------", + "./failing/Fixture/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( m_a == 2 )", + "with expansion:", + " 1 == 2", + "", + "===============================================================================", + "122 test cases - 35 failed (753 assertions - 90 failed)", + "" + ] + testRun = None + for line in lines: + result = obj.parseApprovedLine(line) + if isinstance(result, TestRunData): + testRun = result + + self.assertTrue( isinstance(testRun, TestRunData) ) + newLines = testRun.generateSortedApprovedLines() + #for line in newLines: + # print "L:",line + #print len(lines),",",len(newLines) + expectedLines = [ "", + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", + "CatchSelfTest is a host application.", + "Run with -? for options", + "", + "-------------------------------------------------------------------------------", + "./failing/Fixture/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( m_a == 2 )", + "with expansion:", + " 1 == 2", + "", + "-------------------------------------------------------------------------------", + "./failing/TestClass/failingCase", + "-------------------------------------------------------------------------------", + "ClassTests.cpp:", + "...............................................................................", + "", + "ClassTests.cpp:: FAILED:", + " REQUIRE( s == \"world\" )", + "with expansion:", + " \"hello\" == \"world\"", + "", + "===============================================================================", + "122 test cases - 35 failed (753 assertions - 90 failed)", + "" + ] + self.assertTrue( len(expectedLines) == len(newLines) ) + self.assertTrue( expectedLines == newLines ) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file