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