Split out IStream out of catch_stream.hpp

This commit is contained in:
Martin Hořeňovský 2022-04-16 14:43:40 +02:00
parent 05e85c5652
commit 98bb638fb2
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
14 changed files with 228 additions and 178 deletions

View File

@ -76,6 +76,7 @@ set(INTERNAL_HEADERS
${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp
${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp
${SOURCES_DIR}/internal/catch_istream.hpp
${SOURCES_DIR}/internal/catch_unique_name.hpp
${SOURCES_DIR}/internal/catch_sharding.hpp
${SOURCES_DIR}/generators/catch_generator_exception.hpp
@ -181,6 +182,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp
${SOURCES_DIR}/internal/catch_istream.cpp
${SOURCES_DIR}/generators/internal/catch_generators_combined_tu.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_combined_tu.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp

View File

@ -67,6 +67,7 @@
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_list.hpp>

View File

@ -23,6 +23,7 @@
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <algorithm>
#include <cassert>

View File

@ -15,6 +15,7 @@
#include <catch2/catch_test_case_info.hpp>
#include <catch2/reporters/catch_reporter_helpers.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <algorithm>
#include <cassert>
@ -44,6 +45,8 @@ namespace Catch {
return m_customOptions;
}
ReporterConfig::~ReporterConfig() = default;
AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> const& _infoMessages,
Totals const& _totals )

View File

@ -40,6 +40,10 @@ namespace Catch {
ColourMode colourMode,
std::map<std::string, std::string> customOptions );
ReporterConfig( ReporterConfig&& ) = default;
ReporterConfig& operator=( ReporterConfig&& ) = default;
~ReporterConfig(); // = default
Detail::unique_ptr<IStream> takeStream() &&;
IConfig const* fullConfig() const;
ColourMode colourMode() const;

View File

@ -15,7 +15,7 @@
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_stream.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_platform.hpp>

View File

@ -0,0 +1,158 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_debug_console.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <cstdio>
#include <fstream>
#include <sstream>
#include <vector>
namespace Catch {
Catch::IStream::~IStream() = default;
namespace Detail {
namespace {
template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public std::streambuf {
char data[bufferSize];
WriterF m_writer;
public:
StreamBufImpl() {
setp( data, data + sizeof(data) );
}
~StreamBufImpl() noexcept {
StreamBufImpl::sync();
}
private:
int overflow( int c ) override {
sync();
if( c != EOF ) {
if( pbase() == epptr() )
m_writer( std::string( 1, static_cast<char>( c ) ) );
else
sputc( static_cast<char>( c ) );
}
return 0;
}
int sync() override {
if( pbase() != pptr() ) {
m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
setp( pbase(), epptr() );
}
return 0;
}
};
///////////////////////////////////////////////////////////////////////////
struct OutputDebugWriter {
void operator()( std::string const& str ) {
if ( !str.empty() ) {
writeToDebugConsole( str );
}
}
};
///////////////////////////////////////////////////////////////////////////
class FileStream : public IStream {
std::ofstream m_ofs;
public:
FileStream( std::string const& filename ) {
m_ofs.open( filename.c_str() );
CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' );
}
~FileStream() override = default;
public: // IStream
std::ostream& stream() override {
return m_ofs;
}
};
///////////////////////////////////////////////////////////////////////////
class CoutStream : public IStream {
std::ostream m_os;
public:
// Store the streambuf from cout up-front because
// cout may get redirected when running tests
CoutStream() : m_os( Catch::cout().rdbuf() ) {}
~CoutStream() override = default;
public: // IStream
std::ostream& stream() override { return m_os; }
bool isConsole() const override { return true; }
};
class CerrStream : public IStream {
std::ostream m_os;
public:
// Store the streambuf from cerr up-front because
// cout may get redirected when running tests
CerrStream(): m_os( Catch::cerr().rdbuf() ) {}
~CerrStream() override = default;
public: // IStream
std::ostream& stream() override { return m_os; }
bool isConsole() const override { return true; }
};
///////////////////////////////////////////////////////////////////////////
class DebugOutStream : public IStream {
Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
std::ostream m_os;
public:
DebugOutStream()
: m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
m_os( m_streamBuf.get() )
{}
~DebugOutStream() override = default;
public: // IStream
std::ostream& stream() override { return m_os; }
};
} // unnamed namespace
} // namespace Detail
///////////////////////////////////////////////////////////////////////////
auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream> {
if ( filename.empty() || filename == "-" ) {
return Detail::make_unique<Detail::CoutStream>();
}
if( filename[0] == '%' ) {
if ( filename == "%debug" ) {
return Detail::make_unique<Detail::DebugOutStream>();
} else if ( filename == "%stderr" ) {
return Detail::make_unique<Detail::CerrStream>();
} else if ( filename == "%stdout" ) {
return Detail::make_unique<Detail::CoutStream>();
} else {
CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' );
}
}
return Detail::make_unique<Detail::FileStream>( filename );
}
}

View File

@ -0,0 +1,54 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_ISTREAM_HPP_INCLUDED
#define CATCH_ISTREAM_HPP_INCLUDED
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <iosfwd>
#include <cstddef>
#include <ostream>
#include <string>
namespace Catch {
class IStream {
public:
virtual ~IStream(); // = default
virtual std::ostream& stream() = 0;
/**
* Best guess on whether the instance is writing to a console (e.g. via stdout/stderr)
*
* This is useful for e.g. Win32 colour support, because the Win32
* API manipulates console directly, unlike POSIX escape codes,
* that can be written anywhere.
*
* Due to variety of ways to change where the stdout/stderr is
* _actually_ being written, users should always assume that
* the answer might be wrong.
*/
virtual bool isConsole() const { return false; }
};
/**
* Creates a stream wrapper that writes to specific file.
*
* Also recognizes 4 special filenames
* * `-` for stdout
* * `%stdout` for stdout
* * `%stderr` for stderr
* * `%debug` for platform specific debugging output
*
* \throws if passed an unrecognized %-prefixed stream
*/
auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream>;
}
#endif // CATCH_STREAM_HPP_INCLUDED

View File

@ -5,158 +5,16 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_stream.hpp>
#include <catch2/internal/catch_debug_console.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_singletons.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <cstdio>
#include <fstream>
#include <sstream>
#include <vector>
namespace Catch {
Catch::IStream::~IStream() = default;
namespace Detail {
namespace {
template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public std::streambuf {
char data[bufferSize];
WriterF m_writer;
public:
StreamBufImpl() {
setp( data, data + sizeof(data) );
}
~StreamBufImpl() noexcept {
StreamBufImpl::sync();
}
private:
int overflow( int c ) override {
sync();
if( c != EOF ) {
if( pbase() == epptr() )
m_writer( std::string( 1, static_cast<char>( c ) ) );
else
sputc( static_cast<char>( c ) );
}
return 0;
}
int sync() override {
if( pbase() != pptr() ) {
m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
setp( pbase(), epptr() );
}
return 0;
}
};
///////////////////////////////////////////////////////////////////////////
struct OutputDebugWriter {
void operator()( std::string const& str ) {
if ( !str.empty() ) {
writeToDebugConsole( str );
}
}
};
///////////////////////////////////////////////////////////////////////////
class FileStream : public IStream {
std::ofstream m_ofs;
public:
FileStream( std::string const& filename ) {
m_ofs.open( filename.c_str() );
CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' );
}
~FileStream() override = default;
public: // IStream
std::ostream& stream() override {
return m_ofs;
}
};
///////////////////////////////////////////////////////////////////////////
class CoutStream : public IStream {
std::ostream m_os;
public:
// Store the streambuf from cout up-front because
// cout may get redirected when running tests
CoutStream() : m_os( Catch::cout().rdbuf() ) {}
~CoutStream() override = default;
public: // IStream
std::ostream& stream() override { return m_os; }
bool isConsole() const override { return true; }
};
class CerrStream : public IStream {
std::ostream m_os;
public:
// Store the streambuf from cerr up-front because
// cout may get redirected when running tests
CerrStream(): m_os( Catch::cerr().rdbuf() ) {}
~CerrStream() override = default;
public: // IStream
std::ostream& stream() override { return m_os; }
bool isConsole() const override { return true; }
};
///////////////////////////////////////////////////////////////////////////
class DebugOutStream : public IStream {
Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
std::ostream m_os;
public:
DebugOutStream()
: m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
m_os( m_streamBuf.get() )
{}
~DebugOutStream() override = default;
public: // IStream
std::ostream& stream() override { return m_os; }
};
} // unnamed namespace
} // namespace Detail
///////////////////////////////////////////////////////////////////////////
auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream> {
if ( filename.empty() || filename == "-" ) {
return Detail::make_unique<Detail::CoutStream>();
}
if( filename[0] == '%' ) {
if ( filename == "%debug" ) {
return Detail::make_unique<Detail::DebugOutStream>();
} else if ( filename == "%stderr" ) {
return Detail::make_unique<Detail::CerrStream>();
} else if ( filename == "%stdout" ) {
return Detail::make_unique<Detail::CoutStream>();
} else {
CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' );
}
}
return Detail::make_unique<Detail::FileStream>( filename );
}
// This class encapsulates the idea of a pool of ostringstreams that can be reused.
struct StringStreams {
std::vector<Detail::unique_ptr<std::ostringstream>> m_streams;

View File

@ -9,7 +9,6 @@
#define CATCH_STREAM_HPP_INCLUDED
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <iosfwd>
#include <cstddef>
@ -18,37 +17,6 @@
namespace Catch {
class IStream {
public:
virtual ~IStream(); // = default
virtual std::ostream& stream() = 0;
/**
* Best guess on whether the instance is writing to a console (e.g. via stdout/stderr)
*
* This is useful for e.g. Win32 colour support, because the Win32
* API manipulates console directly, unlike POSIX escape codes,
* that can be written anywhere.
*
* Due to variety of ways to change where the stdout/stderr is
* _actually_ being written, users should always assume that
* the answer might be wrong.
*/
virtual bool isConsole() const { return false; }
};
/**
* Creates a stream wrapper that writes to specific file.
*
* Also recognizes 4 special filenames
* * `-` for stdout
* * `%stdout` for stdout
* * `%stderr` for stderr
* * `%debug` for platform specific debugging output
*
* \throws if passed an unrecognized %-prefixed stream
*/
auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream>;
class ReusableStringStream : Detail::NonCopyable {
std::size_t m_index;
std::ostream* m_oss;

View File

@ -10,7 +10,7 @@
#include <catch2/reporters/catch_reporter_helpers.hpp>
#include <catch2/internal/catch_console_colour.hpp>
#include <catch2/internal/catch_stream.hpp>
#include <catch2/internal/catch_istream.hpp>
namespace Catch {

View File

@ -8,6 +8,7 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/internal/catch_console_colour.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <sstream>

View File

@ -17,7 +17,7 @@
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_stream.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/reporters/catch_reporter_helpers.hpp>
#include <catch2/reporters/catch_reporter_event_listener.hpp>

View File

@ -8,7 +8,7 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/internal/catch_stream.hpp>
#include <catch2/internal/catch_istream.hpp>
TEST_CASE( "Cout stream properly declares it writes to stdout", "[streams]" ) {
REQUIRE( Catch::makeStream( "-" )->isConsole() );