1 /* 2 * Created by Martin on 28/04/2018. 3 * 4 * Distributed under the Boost Software License, Version 1.0. (See accompanying 5 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 8 #include "catch_output_redirect.h" 9 #include "catch_enforce.h" 10 11 #include <cstdio> 12 #include <cstring> 13 #include <fstream> 14 #include <sstream> 15 #include <stdexcept> 16 17 #if defined(CATCH_CONFIG_NEW_CAPTURE) 18 #if defined(_MSC_VER) 19 #include <io.h> //_dup and _dup2 20 #define dup _dup 21 #define dup2 _dup2 22 #define fileno _fileno 23 #else 24 #include <unistd.h> // dup and dup2 25 #endif 26 #endif 27 28 29 namespace Catch { 30 RedirectedStream(std::ostream & originalStream,std::ostream & redirectionStream)31 RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) 32 : m_originalStream( originalStream ), 33 m_redirectionStream( redirectionStream ), 34 m_prevBuf( m_originalStream.rdbuf() ) 35 { 36 m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); 37 } 38 ~RedirectedStream()39 RedirectedStream::~RedirectedStream() { 40 m_originalStream.rdbuf( m_prevBuf ); 41 } 42 RedirectedStdOut()43 RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} str() const44 auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } 45 RedirectedStdErr()46 RedirectedStdErr::RedirectedStdErr() 47 : m_cerr( Catch::cerr(), m_rss.get() ), 48 m_clog( Catch::clog(), m_rss.get() ) 49 {} str() const50 auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } 51 RedirectedStreams(std::string & redirectedCout,std::string & redirectedCerr)52 RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) 53 : m_redirectedCout(redirectedCout), 54 m_redirectedCerr(redirectedCerr) 55 {} 56 ~RedirectedStreams()57 RedirectedStreams::~RedirectedStreams() { 58 m_redirectedCout += m_redirectedStdOut.str(); 59 m_redirectedCerr += m_redirectedStdErr.str(); 60 } 61 62 #if defined(CATCH_CONFIG_NEW_CAPTURE) 63 64 #if defined(_MSC_VER) TempFile()65 TempFile::TempFile() { 66 if (tmpnam_s(m_buffer)) { 67 CATCH_RUNTIME_ERROR("Could not get a temp filename"); 68 } 69 if (fopen_s(&m_file, m_buffer, "w")) { 70 char buffer[100]; 71 if (strerror_s(buffer, errno)) { 72 CATCH_RUNTIME_ERROR("Could not translate errno to a string"); 73 } 74 CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); 75 } 76 } 77 #else TempFile()78 TempFile::TempFile() { 79 m_file = std::tmpfile(); 80 if (!m_file) { 81 CATCH_RUNTIME_ERROR("Could not create a temp file."); 82 } 83 } 84 85 #endif 86 ~TempFile()87 TempFile::~TempFile() { 88 // TBD: What to do about errors here? 89 std::fclose(m_file); 90 // We manually create the file on Windows only, on Linux 91 // it will be autodeleted 92 #if defined(_MSC_VER) 93 std::remove(m_buffer); 94 #endif 95 } 96 97 getFile()98 FILE* TempFile::getFile() { 99 return m_file; 100 } 101 getContents()102 std::string TempFile::getContents() { 103 std::stringstream sstr; 104 char buffer[100] = {}; 105 std::rewind(m_file); 106 while (std::fgets(buffer, sizeof(buffer), m_file)) { 107 sstr << buffer; 108 } 109 return sstr.str(); 110 } 111 OutputRedirect(std::string & stdout_dest,std::string & stderr_dest)112 OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : 113 m_originalStdout(dup(1)), 114 m_originalStderr(dup(2)), 115 m_stdoutDest(stdout_dest), 116 m_stderrDest(stderr_dest) { 117 dup2(fileno(m_stdoutFile.getFile()), 1); 118 dup2(fileno(m_stderrFile.getFile()), 2); 119 } 120 ~OutputRedirect()121 OutputRedirect::~OutputRedirect() { 122 Catch::cout() << std::flush; 123 fflush(stdout); 124 // Since we support overriding these streams, we flush cerr 125 // even though std::cerr is unbuffered 126 Catch::cerr() << std::flush; 127 Catch::clog() << std::flush; 128 fflush(stderr); 129 130 dup2(m_originalStdout, 1); 131 dup2(m_originalStderr, 2); 132 133 m_stdoutDest += m_stdoutFile.getContents(); 134 m_stderrDest += m_stderrFile.getContents(); 135 } 136 137 #endif // CATCH_CONFIG_NEW_CAPTURE 138 139 } // namespace Catch 140 141 #if defined(CATCH_CONFIG_NEW_CAPTURE) 142 #if defined(_MSC_VER) 143 #undef dup 144 #undef dup2 145 #undef fileno 146 #endif 147 #endif 148