1 /* 2 * Created by Phil on 17/01/2011. 3 * Copyright 2011 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 * 8 */ 9 10 #include "catch_common.h" 11 #include "catch_enforce.h" 12 #include "catch_stream.h" 13 #include "catch_debug_console.h" 14 #include "catch_stringref.h" 15 #include "catch_singletons.hpp" 16 17 #include <cstdio> 18 #include <iostream> 19 #include <fstream> 20 #include <sstream> 21 #include <vector> 22 #include <memory> 23 24 namespace Catch { 25 26 Catch::IStream::~IStream() = default; 27 28 namespace detail { namespace { 29 template<typename WriterF, std::size_t bufferSize=256> 30 class StreamBufImpl : public std::streambuf { 31 char data[bufferSize]; 32 WriterF m_writer; 33 34 public: StreamBufImpl()35 StreamBufImpl() { 36 setp( data, data + sizeof(data) ); 37 } 38 ~StreamBufImpl()39 ~StreamBufImpl() noexcept { 40 StreamBufImpl::sync(); 41 } 42 43 private: overflow(int c)44 int overflow( int c ) override { 45 sync(); 46 47 if( c != EOF ) { 48 if( pbase() == epptr() ) 49 m_writer( std::string( 1, static_cast<char>( c ) ) ); 50 else 51 sputc( static_cast<char>( c ) ); 52 } 53 return 0; 54 } 55 sync()56 int sync() override { 57 if( pbase() != pptr() ) { 58 m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); 59 setp( pbase(), epptr() ); 60 } 61 return 0; 62 } 63 }; 64 65 /////////////////////////////////////////////////////////////////////////// 66 67 struct OutputDebugWriter { 68 operator ()Catch::detail::__anon158138930111::OutputDebugWriter69 void operator()( std::string const&str ) { 70 writeToDebugConsole( str ); 71 } 72 }; 73 74 /////////////////////////////////////////////////////////////////////////// 75 76 class FileStream : public IStream { 77 mutable std::ofstream m_ofs; 78 public: FileStream(StringRef filename)79 FileStream( StringRef filename ) { 80 m_ofs.open( filename.c_str() ); 81 CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); 82 } 83 ~FileStream() override = default; 84 public: // IStream stream() const85 std::ostream& stream() const override { 86 return m_ofs; 87 } 88 }; 89 90 /////////////////////////////////////////////////////////////////////////// 91 92 class CoutStream : public IStream { 93 mutable std::ostream m_os; 94 public: 95 // Store the streambuf from cout up-front because 96 // cout may get redirected when running tests CoutStream()97 CoutStream() : m_os( Catch::cout().rdbuf() ) {} 98 ~CoutStream() override = default; 99 100 public: // IStream stream() const101 std::ostream& stream() const override { return m_os; } 102 }; 103 104 /////////////////////////////////////////////////////////////////////////// 105 106 class DebugOutStream : public IStream { 107 std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; 108 mutable std::ostream m_os; 109 public: DebugOutStream()110 DebugOutStream() 111 : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), 112 m_os( m_streamBuf.get() ) 113 {} 114 115 ~DebugOutStream() override = default; 116 117 public: // IStream stream() const118 std::ostream& stream() const override { return m_os; } 119 }; 120 121 }} // namespace anon::detail 122 123 /////////////////////////////////////////////////////////////////////////// 124 makeStream(StringRef const & filename)125 auto makeStream( StringRef const &filename ) -> IStream const* { 126 if( filename.empty() ) 127 return new detail::CoutStream(); 128 else if( filename[0] == '%' ) { 129 if( filename == "%debug" ) 130 return new detail::DebugOutStream(); 131 else 132 CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); 133 } 134 else 135 return new detail::FileStream( filename ); 136 } 137 138 139 // This class encapsulates the idea of a pool of ostringstreams that can be reused. 140 struct StringStreams { 141 std::vector<std::unique_ptr<std::ostringstream>> m_streams; 142 std::vector<std::size_t> m_unused; 143 std::ostringstream m_referenceStream; // Used for copy state/ flags from 144 addCatch::StringStreams145 auto add() -> std::size_t { 146 if( m_unused.empty() ) { 147 m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) ); 148 return m_streams.size()-1; 149 } 150 else { 151 auto index = m_unused.back(); 152 m_unused.pop_back(); 153 return index; 154 } 155 } 156 releaseCatch::StringStreams157 void release( std::size_t index ) { 158 m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state 159 m_unused.push_back(index); 160 } 161 }; 162 ReusableStringStream()163 ReusableStringStream::ReusableStringStream() 164 : m_index( Singleton<StringStreams>::getMutable().add() ), 165 m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() ) 166 {} 167 ~ReusableStringStream()168 ReusableStringStream::~ReusableStringStream() { 169 static_cast<std::ostringstream*>( m_oss )->str(""); 170 m_oss->clear(); 171 Singleton<StringStreams>::getMutable().release( m_index ); 172 } 173 str() const174 auto ReusableStringStream::str() const -> std::string { 175 return static_cast<std::ostringstream*>( m_oss )->str(); 176 } 177 178 179 /////////////////////////////////////////////////////////////////////////// 180 181 182 #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions cout()183 std::ostream& cout() { return std::cout; } cerr()184 std::ostream& cerr() { return std::cerr; } clog()185 std::ostream& clog() { return std::clog; } 186 #endif 187 } 188