/ externals / catch / src / catch2 / internal / catch_output_redirect.cpp
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