Refactored stream related stuff

- simpler, polymorphic hierarchy-based, approach
- less bitty conditionals spread across the code
- all resolved up-front so now config class is immutable
(it had evolved the way it was and in need of a clean-up sweep for a long time)
This commit is contained in:
Phil Nash 2015-09-29 19:21:08 +01:00
parent a0de07d45b
commit d43a47efca
7 changed files with 91 additions and 76 deletions

View File

@ -52,25 +52,11 @@ namespace Catch {
return reporters; return reporters;
} }
void openStreamInto( Ptr<Config> 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<Config> const& config ) { Totals runTests( Ptr<Config> const& config ) {
Ptr<IConfig const> iconfig = config.get(); Ptr<IConfig const> iconfig = config.get();
std::ofstream ofs;
openStreamInto( config, ofs );
Ptr<IStreamingReporter> reporter = makeReporter( config ); Ptr<IStreamingReporter> reporter = makeReporter( config );
reporter = addListeners( iconfig, reporter ); reporter = addListeners( iconfig, reporter );

View File

@ -85,12 +85,11 @@ namespace Catch {
public: public:
Config() Config()
: m_os( Catch::cout().rdbuf() )
{} {}
Config( ConfigData const& data ) Config( ConfigData const& data )
: m_data( data ), : m_data( data ),
m_os( Catch::cout().rdbuf() ) m_stream( openStream() )
{ {
if( !data.testsOrTags.empty() ) { if( !data.testsOrTags.empty() ) {
TestSpecParser parser( ITagAliasRegistry::get() ); TestSpecParser parser( ITagAliasRegistry::get() );
@ -101,12 +100,6 @@ namespace Catch {
} }
virtual ~Config() { 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 { std::string const& getFilename() const {
@ -122,17 +115,6 @@ namespace Catch {
bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } 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<std::string> getReporterNames() const { return m_data.reporterNames; } std::vector<std::string> getReporterNames() const { return m_data.reporterNames; }
int abortAfter() const { return m_data.abortAfter; } int abortAfter() const { return m_data.abortAfter; }
@ -144,7 +126,7 @@ namespace Catch {
// IConfig interface // IConfig interface
virtual bool allowThrows() const { return !m_data.noThrow; } 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 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 includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
@ -154,10 +136,22 @@ namespace Catch {
virtual bool forceColour() const { return m_data.forceColour; } virtual bool forceColour() const { return m_data.forceColour; }
private: 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; ConfigData m_data;
Stream m_stream; std::auto_ptr<IStream const> m_stream;
mutable std::ostream m_os;
TestSpec m_testSpec; TestSpec m_testSpec;
}; };

View File

@ -95,14 +95,6 @@ namespace Catch {
return getCurrentMutableContext(); return getCurrentMutableContext();
} }
Stream createStream( std::string const& streamName ) {
if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
throw std::domain_error( "Unknown stream: " + streamName );
}
void cleanUpContext() { void cleanUpContext() {
delete currentContext; delete currentContext;
currentContext = CATCH_NULL; currentContext = CATCH_NULL;

View File

@ -45,6 +45,7 @@
namespace Catch { namespace Catch {
NonCopyable::~NonCopyable() {} NonCopyable::~NonCopyable() {}
IShared::~IShared() {} IShared::~IShared() {}
IStream::~IStream() CATCH_NOEXCEPT {}
StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
IContext::~IContext() {} IContext::~IContext() {}
IResultCapture::~IResultCapture() {} IResultCapture::~IResultCapture() {}

View File

@ -9,28 +9,52 @@
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#include <streambuf> #include "catch_compiler_capabilities.h"
#include "catch_streambuf.h"
#ifdef __clang__ #include <streambuf>
#pragma clang diagnostic ignored "-Wpadded" #include <ostream>
#endif #include <fstream>
namespace Catch { 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& cout();
std::ostream& cerr(); 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<StreamBufBase> m_streamBuf;
mutable std::ostream m_os;
public:
DebugOutStream();
public: // IStream
virtual std::ostream& stream() const CATCH_OVERRIDE;
};
} }
#endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED

View File

@ -10,7 +10,6 @@
#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
#include "catch_stream.h" #include "catch_stream.h"
#include "catch_streambuf.h"
#include "catch_debugger.h" #include "catch_debugger.h"
#include <stdexcept> #include <stdexcept>
@ -57,6 +56,20 @@ namespace Catch {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
FileStream::FileStream( std::string const& filename ) {
m_ofs.open( filename.c_str() );
if( m_ofs.fail() ) {
std::ostringstream oss;
oss << "Unable to open file: '" << filename << "'";
throw std::domain_error( oss.str() );
}
}
std::ostream& FileStream::stream() const {
return m_ofs;
}
struct OutputDebugWriter { struct OutputDebugWriter {
void operator()( std::string const&str ) { void operator()( std::string const&str ) {
@ -64,22 +77,26 @@ namespace Catch {
} }
}; };
Stream::Stream() DebugOutStream::DebugOutStream()
: streamBuf( CATCH_NULL ), isOwned( false ) : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
m_os( m_streamBuf.get() )
{} {}
Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) std::ostream& DebugOutStream::stream() const {
: streamBuf( _streamBuf ), isOwned( _isOwned ) return m_os;
}
// Store the streambuf from cout up-front because
// cout may get redirected when running tests
CoutStream::CoutStream()
: m_os( Catch::cout().rdbuf() )
{} {}
void Stream::release() { std::ostream& CoutStream::stream() const {
if( isOwned ) { return m_os;
delete streamBuf;
streamBuf = CATCH_NULL;
isOwned = false;
}
} }
#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
std::ostream& cout() { std::ostream& cout() {
return std::cout; return std::cout;

View File

@ -1,2 +1,3 @@
// This file is only here to verify (to the extent possible) the self sufficiency of the header // 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" #include "catch_stream.h"