diff --git a/include/catch_session.hpp b/include/catch_session.hpp index 1f37b5a5..c63f517b 100644 --- a/include/catch_session.hpp +++ b/include/catch_session.hpp @@ -52,25 +52,11 @@ namespace Catch { return reporters; } - void openStreamInto( Ptr const& config, std::ofstream& ofs ) { - // Open output file, if specified - if( !config->getFilename().empty() ) { - ofs.open( config->getFilename().c_str() ); - if( ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << config->getFilename() << "'"; - throw std::domain_error( oss.str() ); - } - config->setStreamBuf( ofs.rdbuf() ); - } - } Totals runTests( Ptr const& config ) { Ptr iconfig = config.get(); - std::ofstream ofs; - openStreamInto( config, ofs ); Ptr reporter = makeReporter( config ); reporter = addListeners( iconfig, reporter ); diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index 6b2b04a3..eca10363 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -85,12 +85,11 @@ namespace Catch { public: Config() - : m_os( Catch::cout().rdbuf() ) {} Config( ConfigData const& data ) : m_data( data ), - m_os( Catch::cout().rdbuf() ) + m_stream( openStream() ) { if( !data.testsOrTags.empty() ) { TestSpecParser parser( ITagAliasRegistry::get() ); @@ -101,12 +100,6 @@ namespace Catch { } virtual ~Config() { - m_os.rdbuf( Catch::cout().rdbuf() ); - m_stream.release(); - } - - void setFilename( std::string const& filename ) { - m_data.outputFilename = filename; } std::string const& getFilename() const { @@ -122,17 +115,6 @@ namespace Catch { bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - void setStreamBuf( std::streambuf* buf ) { - m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() ); - } - - void useStream( std::string const& streamName ) { - Stream stream = createStream( streamName ); - setStreamBuf( stream.streamBuf ); - m_stream.release(); - m_stream = stream; - } - std::vector getReporterNames() const { return m_data.reporterNames; } int abortAfter() const { return m_data.abortAfter; } @@ -144,7 +126,7 @@ namespace Catch { // IConfig interface virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_os; } + virtual std::ostream& stream() const { return m_stream->stream(); } virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } @@ -154,10 +136,22 @@ namespace Catch { virtual bool forceColour() const { return m_data.forceColour; } private: + + IStream const* openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); + } + else + return new FileStream( m_data.outputFilename ); + } ConfigData m_data; - Stream m_stream; - mutable std::ostream m_os; + std::auto_ptr m_stream; TestSpec m_testSpec; }; diff --git a/include/internal/catch_context_impl.hpp b/include/internal/catch_context_impl.hpp index a3ac2ae5..030f29e2 100644 --- a/include/internal/catch_context_impl.hpp +++ b/include/internal/catch_context_impl.hpp @@ -95,14 +95,6 @@ namespace Catch { return getCurrentMutableContext(); } - Stream createStream( std::string const& streamName ) { - if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false ); - if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false ); - if( streamName == "debug" ) return Stream( new StreamBufImpl, true ); - - throw std::domain_error( "Unknown stream: " + streamName ); - } - void cleanUpContext() { delete currentContext; currentContext = CATCH_NULL; diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index 3ff5ac00..170b4807 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -45,6 +45,7 @@ namespace Catch { NonCopyable::~NonCopyable() {} IShared::~IShared() {} + IStream::~IStream() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} IContext::~IContext() {} IResultCapture::~IResultCapture() {} diff --git a/include/internal/catch_stream.h b/include/internal/catch_stream.h index 402a6617..a69c841e 100644 --- a/include/internal/catch_stream.h +++ b/include/internal/catch_stream.h @@ -9,28 +9,52 @@ #ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED -#include +#include "catch_compiler_capabilities.h" +#include "catch_streambuf.h" -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wpadded" -#endif +#include +#include +#include namespace Catch { - class Stream { - public: - Stream(); - Stream( std::streambuf* _streamBuf, bool _isOwned ); - void release(); - - std::streambuf* streamBuf; - - private: - bool isOwned; - }; - std::ostream& cout(); std::ostream& cerr(); + + + struct IStream { + virtual ~IStream() CATCH_NOEXCEPT; + virtual std::ostream& stream() const = 0; + }; + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( std::string const& filename ); + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + CoutStream(); + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + + class DebugOutStream : public IStream { + std::auto_ptr m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream(); + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; } #endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED diff --git a/include/internal/catch_stream.hpp b/include/internal/catch_stream.hpp index 42933487..e6bf7b0c 100644 --- a/include/internal/catch_stream.hpp +++ b/include/internal/catch_stream.hpp @@ -10,7 +10,6 @@ #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #include "catch_stream.h" -#include "catch_streambuf.h" #include "catch_debugger.h" #include @@ -57,29 +56,47 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - struct OutputDebugWriter { + FileStream::FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << filename << "'"; + throw std::domain_error( oss.str() ); + } + } + + std::ostream& FileStream::stream() const { + return m_ofs; + } + + struct OutputDebugWriter { + void operator()( std::string const&str ) { writeToDebugConsole( str ); } }; - - Stream::Stream() - : streamBuf( CATCH_NULL ), isOwned( false ) + + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) {} - - Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) - : streamBuf( _streamBuf ), isOwned( _isOwned ) - {} - - void Stream::release() { - if( isOwned ) { - delete streamBuf; - streamBuf = CATCH_NULL; - isOwned = false; - } + + std::ostream& DebugOutStream::stream() const { + return m_os; } + + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) + {} + std::ostream& CoutStream::stream() const { + return m_os; + } + + #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions std::ostream& cout() { return std::cout; diff --git a/projects/SelfTest/SurrogateCpps/catch_stream.cpp b/projects/SelfTest/SurrogateCpps/catch_stream.cpp index 9911aec6..7aaffa1e 100644 --- a/projects/SelfTest/SurrogateCpps/catch_stream.cpp +++ b/projects/SelfTest/SurrogateCpps/catch_stream.cpp @@ -1,2 +1,3 @@ // This file is only here to verify (to the extent possible) the self sufficiency of the header +#include "catch_suppress_warnings.h" #include "catch_stream.h"