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::__anon6edf36730111::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