// 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 #include #include #include #include #include #include #include #include namespace Catch { Catch::IStream::~IStream() = default; namespace Detail { namespace { template class StreamBufImpl : public std::streambuf { char data[bufferSize]; WriterF m_writer; public: StreamBufImpl() { setp( data, data + sizeof(data) ); } ~StreamBufImpl() noexcept override { StreamBufImpl::sync(); } private: int overflow( int c ) override { sync(); if( c != EOF ) { if( pbase() == epptr() ) m_writer( std::string( 1, static_cast( c ) ) ); else sputc( static_cast( c ) ); } return 0; } int sync() override { if( pbase() != pptr() ) { m_writer( std::string( pbase(), static_cast( 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> m_streamBuf; std::ostream m_os; public: DebugOutStream() : m_streamBuf( Detail::make_unique>() ), 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 { if ( filename.empty() || filename == "-" ) { return Detail::make_unique(); } if( filename[0] == '%' ) { if ( filename == "%debug" ) { return Detail::make_unique(); } else if ( filename == "%stderr" ) { return Detail::make_unique(); } else if ( filename == "%stdout" ) { return Detail::make_unique(); } else { CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' ); } } return Detail::make_unique( filename ); } }