aboutsummaryrefslogtreecommitdiffstats
path: root/src/catch2/internal/catch_istream.cpp
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-29 19:25:29 +0100
commitbc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch)
tree1e629e7b46b1d9972a973bc93fd100bcebd395be /src/catch2/internal/catch_istream.cpp
downloadnihil-vendor/catch2/3.8.1.tar.gz
nihil-vendor/catch2/3.8.1.tar.bz2
Diffstat (limited to 'src/catch2/internal/catch_istream.cpp')
-rw-r--r--src/catch2/internal/catch_istream.cpp154
1 files changed, 154 insertions, 0 deletions
diff --git a/src/catch2/internal/catch_istream.cpp b/src/catch2/internal/catch_istream.cpp
new file mode 100644
index 0000000..2867ce7
--- /dev/null
+++ b/src/catch2/internal/catch_istream.cpp
@@ -0,0 +1,154 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.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 final : 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<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 final : 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 << '\'' );
+ m_ofs << std::unitbuf;
+ }
+ public: // IStream
+ std::ostream& stream() override {
+ return m_ofs;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class CoutStream final : 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() ) {}
+
+ 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() ) {}
+
+ public: // IStream
+ std::ostream& stream() override { return m_os; }
+ bool isConsole() const override { return true; }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ class DebugOutStream final : 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() )
+ {}
+
+ 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 );
+ }
+
+}