diff --git a/Test/Test.xcodeproj/project.pbxproj b/Test/Test.xcodeproj/project.pbxproj index 7294df0b..951515e7 100644 --- a/Test/Test.xcodeproj/project.pbxproj +++ b/Test/Test.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ /* Begin PBXFileReference section */ 4A15D2B712E0418F0005EB03 /* catch_self_test.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = catch_self_test.hpp; path = ../internal/catch_self_test.hpp; sourceTree = SOURCE_ROOT; }; + 4A15D4A812E4DF0D0005EB03 /* catch_stream.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = catch_stream.hpp; path = ../internal/catch_stream.hpp; sourceTree = SOURCE_ROOT; }; 4A302DE312D5114900C84B67 /* catch.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = catch.hpp; path = ../catch.hpp; sourceTree = SOURCE_ROOT; }; 4A33BCE512CE7F500052A211 /* catch_hub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = catch_hub.h; path = ../internal/catch_hub.h; sourceTree = SOURCE_ROOT; }; 4A33BCF912CE80EC0052A211 /* catch_interfaces_reporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = catch_interfaces_reporter.h; path = ../internal/catch_interfaces_reporter.h; sourceTree = SOURCE_ROOT; }; @@ -163,6 +164,7 @@ 4A6D514B12C8A547008F0415 /* catch_debugger.hpp */, 4AFC341612809A36003A0C29 /* catch_common.h */, 4A992A6512B2156C002B7B66 /* catch_xmlwriter.hpp */, + 4A15D4A812E4DF0D0005EB03 /* catch_stream.hpp */, ); name = support; sourceTree = ""; diff --git a/Test/TestMain.cpp b/Test/TestMain.cpp index 0f9706e5..1d6a4655 100644 --- a/Test/TestMain.cpp +++ b/Test/TestMain.cpp @@ -23,8 +23,7 @@ TEST_CASE( "selftest/main", "Runs all Catch self tests and checks their results" CHECK( runner.getReporter().getSucceeded() == 53 ); CHECK( runner.getReporter().getFailed() == 0 ); - runner.runMatching( "./failing/*" ); - + runner.runMatching( "./failing/*" ); CHECK( runner.getReporter().getSucceeded() == 0 ); CHECK( runner.getReporter().getFailed() == 53 ); } diff --git a/catch_reporter_xml.hpp b/catch_reporter_xml.hpp index 37b85621..95f92619 100644 --- a/catch_reporter_xml.hpp +++ b/catch_reporter_xml.hpp @@ -23,13 +23,17 @@ namespace Catch { public: /////////////////////////////////////////////////////////////////////////// - XmlReporter( const IReporterConfig& config ) - : m_config( config ) + XmlReporter + ( + const IReporterConfig& config + ) + : m_config( config ) { } /////////////////////////////////////////////////////////////////////////// - static std::string getDescription() + static std::string getDescription + () { return "Reports test results as an XML document"; } @@ -37,14 +41,19 @@ namespace Catch private: // IReporter /////////////////////////////////////////////////////////////////////////// - virtual void StartTesting() + virtual void StartTesting + () { m_xml = XmlWriter( m_config.stream() ); m_xml.startElement( "AllTests" ); } /////////////////////////////////////////////////////////////////////////// - virtual void EndTesting( std::size_t succeeded, std::size_t failed ) + virtual void EndTesting + ( + std::size_t succeeded, + std::size_t failed + ) { m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", succeeded ) @@ -52,14 +61,22 @@ namespace Catch } /////////////////////////////////////////////////////////////////////////// - virtual void StartGroup( const std::string& groupName ) + virtual void StartGroup + ( + const std::string& groupName + ) { m_xml.startElement( "Group" ) .writeAttribute( "name", groupName ); } /////////////////////////////////////////////////////////////////////////// - virtual void EndGroup( const std::string& /*groupName*/, std::size_t succeeded, std::size_t failed ) + virtual void EndGroup + ( + const std::string& /*groupName*/, + std::size_t succeeded, + std::size_t failed + ) { m_xml.scopedElement( "OverallResults" ) .writeAttribute( "successes", succeeded ) diff --git a/catch_runner.hpp b/catch_runner.hpp index 4e4947fa..c5685a96 100644 --- a/catch_runner.hpp +++ b/catch_runner.hpp @@ -50,7 +50,7 @@ namespace Catch << "\t-l, --list [xml]\n" << "\t-t, --test [...]\n" << "\t-r, --reporter \n" - << "\t-o, --out \n" + << "\t-o, --out |<%stream name>\n" << "\t-s, --success\n" << "\t-b, --break\n\n" << "For more detail usage please see: https://github.com/philsquared/Catch/wiki/Command-line" << std::endl; diff --git a/internal/catch_commandline.hpp b/internal/catch_commandline.hpp index 3f83813b..db0a0012 100644 --- a/internal/catch_commandline.hpp +++ b/internal/catch_commandline.hpp @@ -152,7 +152,10 @@ namespace Catch case modeOutput: if( m_args.size() == 0 ) return setErrorMode( m_command + " expected filename" ); - m_config.setFilename( m_args[0] ); + if( m_args[0][0] == '%' ) + m_config.useStream( m_args[0].substr( 1 ) ); + else + m_config.setFilename( m_args[0] ); break; case modeSuccess: if( m_args.size() != 0 ) diff --git a/internal/catch_config.hpp b/internal/catch_config.hpp index aba2393f..90c130fa 100644 --- a/internal/catch_config.hpp +++ b/internal/catch_config.hpp @@ -23,6 +23,7 @@ namespace Catch { + class Config : public IReporterConfig { private: @@ -59,10 +60,17 @@ namespace Catch m_listSpec( List::None ), m_shouldDebugBreak( false ), m_showHelp( false ), - m_os( std::cout.rdbuf() ), + m_streambuf( std::cout.rdbuf() ), + m_os( m_streambuf ), m_includeWhat( Include::FailedOnly ) {} + /////////////////////////////////////////////////////////////////////////// + ~Config() + { + setStreamBuf( NULL ); + } + /////////////////////////////////////////////////////////////////////////// void setReporter( const std::string& reporterName ) { @@ -167,14 +175,25 @@ namespace Catch virtual std::ostream& stream() const { return m_os; - } + } /////////////////////////////////////////////////////////////////////////// void setStreamBuf( std::streambuf* buf ) { - m_os.rdbuf( buf ); + // Delete previous stream buf if we own it + if( m_streambuf && dynamic_cast( m_streambuf ) ) + delete m_streambuf; + + m_streambuf = buf; + m_os.rdbuf( buf ? buf : std::cout.rdbuf() ); } + /////////////////////////////////////////////////////////////////////////// + void useStream( const std::string& streamName ) + { + setStreamBuf( Hub::createStreamBuf( streamName ) ); + } + /////////////////////////////////////////////////////////////////////////// virtual bool includeSuccessfulResults() const { @@ -189,6 +208,7 @@ namespace Catch std::vector m_testSpecs; bool m_shouldDebugBreak; bool m_showHelp; + std::streambuf* m_streambuf; mutable std::ostream m_os; Include::What m_includeWhat; }; diff --git a/internal/catch_debugger.hpp b/internal/catch_debugger.hpp index a5e998dd..5edcf5ec 100644 --- a/internal/catch_debugger.hpp +++ b/internal/catch_debugger.hpp @@ -15,6 +15,8 @@ #ifndef TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED +#include + #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) #include @@ -83,5 +85,14 @@ inline void DebugBreak(){} #endif +inline void writeToDebugConsole( const std::string& text ) +{ +#ifdef _WIN32 + ::OutputDebugStringA( text.get() ); +#else + // !TBD: Need a version for Mac/ XCode and other IDEs + std::cout << text; +#endif +} #endif // TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED \ No newline at end of file diff --git a/internal/catch_hub.h b/internal/catch_hub.h index c49a130f..9f8537bb 100644 --- a/internal/catch_hub.h +++ b/internal/catch_hub.h @@ -23,6 +23,10 @@ namespace Catch struct IResultCapture; struct ITestCaseRegistry; struct IRunner; + + class StreamBufBase : public std::streambuf + { + }; class Hub { @@ -49,6 +53,10 @@ namespace Catch static ITestCaseRegistry& getTestCaseRegistry (); + static std::streambuf* createStreamBuf + ( const std::string& streamName + ); + static IRunner& getRunner (); diff --git a/internal/catch_hub_impl.hpp b/internal/catch_hub_impl.hpp index 8d0f81fd..ad642a86 100644 --- a/internal/catch_hub_impl.hpp +++ b/internal/catch_hub_impl.hpp @@ -13,9 +13,10 @@ #include "catch_reporter_registry.hpp" #include "catch_test_case_registry_impl.hpp" #include "catch_runner_impl.hpp" +#include "catch_stream.hpp" namespace Catch -{ +{ /////////////////////////////////////////////////////////////////////////// Hub::Hub () @@ -70,4 +71,18 @@ namespace Catch { return *me().m_testCaseRegistry.get(); } + + /////////////////////////////////////////////////////////////////////////// + std::streambuf* Hub::createStreamBuf + ( + const std::string& streamName + ) + { + if( streamName == "stdout" ) return std::cout.rdbuf(); + if( streamName == "stderr" ) return std::cerr.rdbuf(); + if( streamName == "debug" ) return new StreamBufImpl; + + throw std::domain_error( "Unknown stream: " + streamName ); + } + } diff --git a/internal/catch_stream.hpp b/internal/catch_stream.hpp new file mode 100644 index 00000000..252ccb5d --- /dev/null +++ b/internal/catch_stream.hpp @@ -0,0 +1,71 @@ +/* + * catch_stream.hpp + * Catch + * + * Created by Phil on 17/01/2011. + * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + * + */ + +#ifndef TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +#include + +namespace Catch +{ + template + class StreamBufImpl : public StreamBufBase + { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() + { + setp( data, data + sizeof(data) ); + } + ~StreamBufImpl() + { + sync(); + } + + private: + int overflow( int c ) + { + sync(); + + if( c != EOF ) + { + if( pbase() == epptr() ) + m_writer( std::string( 1, (char)c ) ); + else + sputc( c ); + } + return 0; + } + + int sync() + { + if( pbase() != pptr() ) + { + m_writer( std::string( pbase(), pptr() - pbase() ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + struct OutputDebugWriter + { + void operator()( const std::string &str ) + { + writeToDebugConsole( str ); + } + }; +} + +#endif // TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED \ No newline at end of file