/ externals / catch / src / catch2 / internal / catch_istream.cpp
catch_istream.cpp
  1  
  2  //              Copyright Catch2 Authors
  3  // Distributed under the Boost Software License, Version 1.0.
  4  //   (See accompanying file LICENSE.txt or copy at
  5  //        https://www.boost.org/LICENSE_1_0.txt)
  6  
  7  // SPDX-License-Identifier: BSL-1.0
  8  
  9  #include <catch2/internal/catch_istream.hpp>
 10  #include <catch2/internal/catch_enforce.hpp>
 11  #include <catch2/internal/catch_debug_console.hpp>
 12  #include <catch2/internal/catch_unique_ptr.hpp>
 13  #include <catch2/internal/catch_stdstreams.hpp>
 14  
 15  #include <cstdio>
 16  #include <fstream>
 17  #include <sstream>
 18  #include <vector>
 19  
 20  namespace Catch {
 21  
 22      Catch::IStream::~IStream() = default;
 23  
 24  namespace Detail {
 25      namespace {
 26          template<typename WriterF, std::size_t bufferSize=256>
 27          class StreamBufImpl final : public std::streambuf {
 28              char data[bufferSize];
 29              WriterF m_writer;
 30  
 31          public:
 32              StreamBufImpl() {
 33                  setp( data, data + sizeof(data) );
 34              }
 35  
 36              ~StreamBufImpl() noexcept override {
 37                  StreamBufImpl::sync();
 38              }
 39  
 40          private:
 41              int overflow( int c ) override {
 42                  sync();
 43  
 44                  if( c != EOF ) {
 45                      if( pbase() == epptr() )
 46                          m_writer( std::string( 1, static_cast<char>( c ) ) );
 47                      else
 48                          sputc( static_cast<char>( c ) );
 49                  }
 50                  return 0;
 51              }
 52  
 53              int sync() override {
 54                  if( pbase() != pptr() ) {
 55                      m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
 56                      setp( pbase(), epptr() );
 57                  }
 58                  return 0;
 59              }
 60          };
 61  
 62          ///////////////////////////////////////////////////////////////////////////
 63  
 64          struct OutputDebugWriter {
 65  
 66              void operator()( std::string const& str ) {
 67                  if ( !str.empty() ) {
 68                      writeToDebugConsole( str );
 69                  }
 70              }
 71          };
 72  
 73          ///////////////////////////////////////////////////////////////////////////
 74  
 75          class FileStream final : public IStream {
 76              std::ofstream m_ofs;
 77          public:
 78              FileStream( std::string const& filename ) {
 79                  m_ofs.open( filename.c_str() );
 80                  CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' );
 81                  m_ofs << std::unitbuf;
 82              }
 83              ~FileStream() override = default;
 84          public: // IStream
 85              std::ostream& stream() override {
 86                  return m_ofs;
 87              }
 88          };
 89  
 90          ///////////////////////////////////////////////////////////////////////////
 91  
 92          class CoutStream final : public IStream {
 93              std::ostream m_os;
 94          public:
 95              // Store the streambuf from cout up-front because
 96              // cout may get redirected when running tests
 97              CoutStream() : m_os( Catch::cout().rdbuf() ) {}
 98              ~CoutStream() override = default;
 99  
100          public: // IStream
101              std::ostream& stream() override { return m_os; }
102              bool isConsole() const override { return true; }
103          };
104  
105          class CerrStream : public IStream {
106              std::ostream m_os;
107  
108          public:
109              // Store the streambuf from cerr up-front because
110              // cout may get redirected when running tests
111              CerrStream(): m_os( Catch::cerr().rdbuf() ) {}
112              ~CerrStream() override = default;
113  
114          public: // IStream
115              std::ostream& stream() override { return m_os; }
116              bool isConsole() const override { return true; }
117          };
118  
119          ///////////////////////////////////////////////////////////////////////////
120  
121          class DebugOutStream final : public IStream {
122              Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
123              std::ostream m_os;
124          public:
125              DebugOutStream()
126              :   m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
127                  m_os( m_streamBuf.get() )
128              {}
129  
130              ~DebugOutStream() override = default;
131  
132          public: // IStream
133              std::ostream& stream() override { return m_os; }
134          };
135  
136      } // unnamed namespace
137  } // namespace Detail
138  
139      ///////////////////////////////////////////////////////////////////////////
140  
141      auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream> {
142          if ( filename.empty() || filename == "-" ) {
143              return Detail::make_unique<Detail::CoutStream>();
144          }
145          if( filename[0] == '%' ) {
146              if ( filename == "%debug" ) {
147                  return Detail::make_unique<Detail::DebugOutStream>();
148              } else if ( filename == "%stderr" ) {
149                  return Detail::make_unique<Detail::CerrStream>();
150              } else if ( filename == "%stdout" ) {
151                  return Detail::make_unique<Detail::CoutStream>();
152              } else {
153                  CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' );
154              }
155          }
156          return Detail::make_unique<Detail::FileStream>( filename );
157      }
158  
159  }