diff --git a/src/catch2/internal/catch_output_redirect.cpp b/src/catch2/internal/catch_output_redirect.cpp index 9f0538beab..68b741e546 100644 --- a/src/catch2/internal/catch_output_redirect.cpp +++ b/src/catch2/internal/catch_output_redirect.cpp @@ -70,6 +70,8 @@ namespace Catch { class StreamRedirect : public OutputRedirectNew { ReusableStringStream m_redirectedOut, m_redirectedErr; RedirectedStreamNew m_cout, m_cerr, m_clog; + + public: StreamRedirect(): m_cout( Catch::cout(), m_redirectedOut.get() ), m_cerr( Catch::cerr(), m_redirectedErr.get() ), @@ -93,6 +95,9 @@ namespace Catch { } }; + +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + // Windows's implementation of std::tmpfile is terrible (it tries // to create a file inside system folder, thus requiring elevated // privileges for the binary), so we have to use tmpnam(_s) and @@ -214,11 +219,33 @@ namespace Catch { } }; +#endif // CATCH_CONFIG_NEW_CAPTURE + } // end namespace + bool isRedirectAvailable( OutputRedirectNew::Kind kind ) { + switch ( kind ) { + // These two are always available + case OutputRedirectNew::None: + case OutputRedirectNew::Streams: + return true; +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + case OutputRedirectNew::FileDescriptors: + return true; +#endif + default: + return false; + } + } + Detail::unique_ptr makeOutputRedirect( bool actual ) { if ( actual ) { + // TODO: Clean this up later +#if defined( CATCH_CONFIG_NEW_CAPTURE ) return Detail::make_unique(); +#else + return Detail::make_unique(); +#endif } else { return Detail::make_unique(); } @@ -234,85 +261,6 @@ namespace Catch { OutputRedirectNew::~OutputRedirectNew() = default; - -#if defined(CATCH_CONFIG_NEW_CAPTURE) - -#if defined(_MSC_VER) - TempFile::TempFile() { - if (tmpnam_s(m_buffer)) { - CATCH_RUNTIME_ERROR("Could not get a temp filename"); - } - if (fopen_s(&m_file, m_buffer, "w+")) { - char buffer[100]; - if (strerror_s(buffer, errno)) { - CATCH_RUNTIME_ERROR("Could not translate errno to a string"); - } - CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); - } - } -#else - TempFile::TempFile() { - m_file = std::tmpfile(); - if (!m_file) { - CATCH_RUNTIME_ERROR("Could not create a temp file."); - } - } - -#endif - - TempFile::~TempFile() { - // TBD: What to do about errors here? - std::fclose(m_file); - // We manually create the file on Windows only, on Linux - // it will be autodeleted -#if defined(_MSC_VER) - std::remove(m_buffer); -#endif - } - - - FILE* TempFile::getFile() { - return m_file; - } - - std::string TempFile::getContents() { - std::stringstream sstr; - char buffer[100] = {}; - std::rewind(m_file); - while (std::fgets(buffer, sizeof(buffer), m_file)) { - sstr << buffer; - } - return sstr.str(); - } - - OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : - m_originalStdout(dup(1)), - m_originalStderr(dup(2)), - m_stdoutDest(stdout_dest), - m_stderrDest(stderr_dest) { - dup2(fileno(m_stdoutFile.getFile()), 1); - dup2(fileno(m_stderrFile.getFile()), 2); - } - - OutputRedirect::~OutputRedirect() { - Catch::cout() << std::flush; - fflush(stdout); - // Since we support overriding these streams, we flush cerr - // even though std::cerr is unbuffered - Catch::cerr() << std::flush; - Catch::clog() << std::flush; - fflush(stderr); - - dup2(m_originalStdout, 1); - dup2(m_originalStderr, 2); - - m_stdoutDest += m_stdoutFile.getContents(); - m_stderrDest += m_stderrFile.getContents(); - } - -#endif // CATCH_CONFIG_NEW_CAPTURE - - // TODO: pass pointer? RedirectGuard::RedirectGuard( bool activate, OutputRedirectNew& redirectImpl ): diff --git a/src/catch2/internal/catch_output_redirect.hpp b/src/catch2/internal/catch_output_redirect.hpp index c527304079..7d28b2236f 100644 --- a/src/catch2/internal/catch_output_redirect.hpp +++ b/src/catch2/internal/catch_output_redirect.hpp @@ -25,6 +25,15 @@ namespace Catch { virtual void activateImpl() = 0; virtual void deactivateImpl() = 0; public: + enum Kind { + //! No redirect (noop implementation) + None, + //! Redirect std::cout/std::cerr/std::clog streams internally + Streams, + //! Redirect the stdout/stderr file descriptors into files + FileDescriptors, + }; + virtual ~OutputRedirectNew(); // = default; // TODO: check that redirect is not active before retrieving stdout/stderr? @@ -43,6 +52,7 @@ namespace Catch { } }; + bool isRedirectAvailable( OutputRedirectNew::Kind kind); Detail::unique_ptr makeOutputRedirect( bool actual ); class RedirectGuard { @@ -65,55 +75,6 @@ namespace Catch { RedirectGuard scopedActivate( OutputRedirectNew& redirectImpl ); RedirectGuard scopedDeactivate( OutputRedirectNew& redirectImpl ); -#if defined(CATCH_CONFIG_NEW_CAPTURE) - - // Windows's implementation of std::tmpfile is terrible (it tries - // to create a file inside system folder, thus requiring elevated - // privileges for the binary), so we have to use tmpnam(_s) and - // create the file ourselves there. - class TempFile { - public: - TempFile(TempFile const&) = delete; - TempFile& operator=(TempFile const&) = delete; - TempFile(TempFile&&) = delete; - TempFile& operator=(TempFile&&) = delete; - - TempFile(); - ~TempFile(); - - std::FILE* getFile(); - std::string getContents(); - - private: - std::FILE* m_file = nullptr; - #if defined(_MSC_VER) - char m_buffer[L_tmpnam] = { 0 }; - #endif - }; - - - class OutputRedirect { - public: - OutputRedirect(OutputRedirect const&) = delete; - OutputRedirect& operator=(OutputRedirect const&) = delete; - OutputRedirect(OutputRedirect&&) = delete; - OutputRedirect& operator=(OutputRedirect&&) = delete; - - - OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); - ~OutputRedirect(); - - private: - int m_originalStdout = -1; - int m_originalStderr = -1; - TempFile m_stdoutFile; - TempFile m_stderrFile; - std::string& m_stdoutDest; - std::string& m_stderrDest; - }; - -#endif - } // end namespace Catch #endif // CATCH_OUTPUT_REDIRECT_HPP_INCLUDED