mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-11-03 21:49:32 +01:00 
			
		
		
		
	v3.7.0
This commit is contained in:
		@@ -6,8 +6,8 @@
 | 
			
		||||
 | 
			
		||||
// SPDX-License-Identifier: BSL-1.0
 | 
			
		||||
 | 
			
		||||
//  Catch v3.6.0
 | 
			
		||||
//  Generated: 2024-05-05 20:53:27.562886
 | 
			
		||||
//  Catch v3.7.0
 | 
			
		||||
//  Generated: 2024-08-14 12:04:53.604337
 | 
			
		||||
//  ----------------------------------------------------------
 | 
			
		||||
//  This file is an amalgamation of multiple different files.
 | 
			
		||||
//  You probably shouldn't edit it directly.
 | 
			
		||||
@@ -128,7 +128,13 @@ namespace Catch {
 | 
			
		||||
namespace Catch {
 | 
			
		||||
    namespace Benchmark {
 | 
			
		||||
        namespace Detail {
 | 
			
		||||
            struct do_nothing {
 | 
			
		||||
                void operator()() const {}
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            BenchmarkFunction::callable::~callable() = default;
 | 
			
		||||
            BenchmarkFunction::BenchmarkFunction():
 | 
			
		||||
                f( new model<do_nothing>{ {} } ){}
 | 
			
		||||
        } // namespace Detail
 | 
			
		||||
    } // namespace Benchmark
 | 
			
		||||
} // namespace Catch
 | 
			
		||||
@@ -1040,6 +1046,7 @@ namespace Catch {
 | 
			
		||||
                    m_messages.back().message += " := ";
 | 
			
		||||
                    start = pos;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            default:; // noop
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -2273,7 +2280,7 @@ namespace Catch {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Version const& libraryVersion() {
 | 
			
		||||
        static Version version( 3, 6, 0, "", 0 );
 | 
			
		||||
        static Version version( 3, 7, 0, "", 0 );
 | 
			
		||||
        return version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -4808,138 +4815,328 @@ namespace Catch {
 | 
			
		||||
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <iosfwd>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#if defined(CATCH_CONFIG_NEW_CAPTURE)
 | 
			
		||||
    #if defined(_MSC_VER)
 | 
			
		||||
    #include <io.h>      //_dup and _dup2
 | 
			
		||||
    #define dup _dup
 | 
			
		||||
    #define dup2 _dup2
 | 
			
		||||
    #define fileno _fileno
 | 
			
		||||
    #else
 | 
			
		||||
    #include <unistd.h>  // dup and dup2
 | 
			
		||||
    #endif
 | 
			
		||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
 | 
			
		||||
#    if defined( _MSC_VER )
 | 
			
		||||
#        include <io.h> //_dup and _dup2
 | 
			
		||||
#        define dup _dup
 | 
			
		||||
#        define dup2 _dup2
 | 
			
		||||
#        define fileno _fileno
 | 
			
		||||
#    else
 | 
			
		||||
#        include <unistd.h> // dup and dup2
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Catch {
 | 
			
		||||
 | 
			
		||||
    RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
 | 
			
		||||
    :   m_originalStream( originalStream ),
 | 
			
		||||
        m_redirectionStream( redirectionStream ),
 | 
			
		||||
        m_prevBuf( m_originalStream.rdbuf() )
 | 
			
		||||
    {
 | 
			
		||||
        m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
 | 
			
		||||
    }
 | 
			
		||||
    namespace {
 | 
			
		||||
        //! A no-op implementation, used if no reporter wants output
 | 
			
		||||
        //! redirection.
 | 
			
		||||
        class NoopRedirect : public OutputRedirect {
 | 
			
		||||
            void activateImpl() override {}
 | 
			
		||||
            void deactivateImpl() override {}
 | 
			
		||||
            std::string getStdout() override { return {}; }
 | 
			
		||||
            std::string getStderr() override { return {}; }
 | 
			
		||||
            void clearBuffers() override {}
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    RedirectedStream::~RedirectedStream() {
 | 
			
		||||
        m_originalStream.rdbuf( m_prevBuf );
 | 
			
		||||
    }
 | 
			
		||||
        /**
 | 
			
		||||
         * Redirects specific stream's rdbuf with another's.
 | 
			
		||||
         *
 | 
			
		||||
         * Redirection can be stopped and started on-demand, assumes
 | 
			
		||||
         * that the underlying stream's rdbuf aren't changed by other
 | 
			
		||||
         * users.
 | 
			
		||||
         */
 | 
			
		||||
        class RedirectedStreamNew {
 | 
			
		||||
            std::ostream& m_originalStream;
 | 
			
		||||
            std::ostream& m_redirectionStream;
 | 
			
		||||
            std::streambuf* m_prevBuf;
 | 
			
		||||
 | 
			
		||||
    RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
 | 
			
		||||
    auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
 | 
			
		||||
        public:
 | 
			
		||||
            RedirectedStreamNew( std::ostream& originalStream,
 | 
			
		||||
                                 std::ostream& redirectionStream ):
 | 
			
		||||
                m_originalStream( originalStream ),
 | 
			
		||||
                m_redirectionStream( redirectionStream ),
 | 
			
		||||
                m_prevBuf( m_originalStream.rdbuf() ) {}
 | 
			
		||||
 | 
			
		||||
    RedirectedStdErr::RedirectedStdErr()
 | 
			
		||||
    :   m_cerr( Catch::cerr(), m_rss.get() ),
 | 
			
		||||
        m_clog( Catch::clog(), m_rss.get() )
 | 
			
		||||
    {}
 | 
			
		||||
    auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
 | 
			
		||||
 | 
			
		||||
    RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
 | 
			
		||||
    :   m_redirectedCout(redirectedCout),
 | 
			
		||||
        m_redirectedCerr(redirectedCerr)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    RedirectedStreams::~RedirectedStreams() {
 | 
			
		||||
        m_redirectedCout += m_redirectedStdOut.str();
 | 
			
		||||
        m_redirectedCerr += m_redirectedStdErr.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#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");
 | 
			
		||||
            void startRedirect() {
 | 
			
		||||
                m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
 | 
			
		||||
            }
 | 
			
		||||
            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.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
            void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
        /**
 | 
			
		||||
         * Redirects the `std::cout`, `std::cerr`, `std::clog` streams,
 | 
			
		||||
         * but does not touch the actual `stdout`/`stderr` file descriptors.
 | 
			
		||||
         */
 | 
			
		||||
        class StreamRedirect : public OutputRedirect {
 | 
			
		||||
            ReusableStringStream m_redirectedOut, m_redirectedErr;
 | 
			
		||||
            RedirectedStreamNew m_cout, m_cerr, m_clog;
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
        public:
 | 
			
		||||
            StreamRedirect():
 | 
			
		||||
                m_cout( Catch::cout(), m_redirectedOut.get() ),
 | 
			
		||||
                m_cerr( Catch::cerr(), m_redirectedErr.get() ),
 | 
			
		||||
                m_clog( Catch::clog(), m_redirectedErr.get() ) {}
 | 
			
		||||
 | 
			
		||||
            void activateImpl() override {
 | 
			
		||||
                m_cout.startRedirect();
 | 
			
		||||
                m_cerr.startRedirect();
 | 
			
		||||
                m_clog.startRedirect();
 | 
			
		||||
            }
 | 
			
		||||
            void deactivateImpl() override {
 | 
			
		||||
                m_cout.stopRedirect();
 | 
			
		||||
                m_cerr.stopRedirect();
 | 
			
		||||
                m_clog.stopRedirect();
 | 
			
		||||
            }
 | 
			
		||||
            std::string getStdout() override { return m_redirectedOut.str(); }
 | 
			
		||||
            std::string getStderr() override { return m_redirectedErr.str(); }
 | 
			
		||||
            void clearBuffers() override {
 | 
			
		||||
                m_redirectedOut.str( "" );
 | 
			
		||||
                m_redirectedErr.str( "" );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    FILE* TempFile::getFile() {
 | 
			
		||||
        return m_file;
 | 
			
		||||
    }
 | 
			
		||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
        // 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;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
#    if defined( _MSC_VER )
 | 
			
		||||
            TempFile() {
 | 
			
		||||
                if ( tmpnam_s( m_buffer ) ) {
 | 
			
		||||
                    CATCH_RUNTIME_ERROR( "Could not get a temp filename" );
 | 
			
		||||
                }
 | 
			
		||||
                if ( fopen_s( &m_file, m_buffer, "wb+" ) ) {
 | 
			
		||||
                    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() {
 | 
			
		||||
                m_file = std::tmpfile();
 | 
			
		||||
                if ( !m_file ) {
 | 
			
		||||
                    CATCH_RUNTIME_ERROR( "Could not create a temp file." );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
#    endif
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
            ~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
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        dup2(m_originalStdout, 1);
 | 
			
		||||
        dup2(m_originalStderr, 2);
 | 
			
		||||
            std::FILE* getFile() { return m_file; }
 | 
			
		||||
            std::string getContents() {
 | 
			
		||||
                ReusableStringStream sstr;
 | 
			
		||||
                constexpr long buffer_size = 100;
 | 
			
		||||
                char buffer[buffer_size + 1] = {};
 | 
			
		||||
                long current_pos = ftell( m_file );
 | 
			
		||||
                CATCH_ENFORCE( current_pos >= 0,
 | 
			
		||||
                               "ftell failed, errno: " << errno );
 | 
			
		||||
                std::rewind( m_file );
 | 
			
		||||
                while ( current_pos > 0 ) {
 | 
			
		||||
                    auto read_characters =
 | 
			
		||||
                        std::fread( buffer,
 | 
			
		||||
                                    1,
 | 
			
		||||
                                    std::min( buffer_size, current_pos ),
 | 
			
		||||
                                    m_file );
 | 
			
		||||
                    buffer[read_characters] = '\0';
 | 
			
		||||
                    sstr << buffer;
 | 
			
		||||
                    current_pos -= static_cast<long>( read_characters );
 | 
			
		||||
                }
 | 
			
		||||
                return sstr.str();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        m_stdoutDest += m_stdoutFile.getContents();
 | 
			
		||||
        m_stderrDest += m_stderrFile.getContents();
 | 
			
		||||
    }
 | 
			
		||||
            void clear() { std::rewind( m_file ); }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            std::FILE* m_file = nullptr;
 | 
			
		||||
            char m_buffer[L_tmpnam] = { 0 };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Redirects the actual `stdout`/`stderr` file descriptors.
 | 
			
		||||
         *
 | 
			
		||||
         * Works by replacing the file descriptors numbered 1 and 2
 | 
			
		||||
         * with an open temporary file.
 | 
			
		||||
         */
 | 
			
		||||
        class FileRedirect : public OutputRedirect {
 | 
			
		||||
            TempFile m_outFile, m_errFile;
 | 
			
		||||
            int m_originalOut = -1;
 | 
			
		||||
            int m_originalErr = -1;
 | 
			
		||||
 | 
			
		||||
            // Flushes cout/cerr/clog streams and stdout/stderr FDs
 | 
			
		||||
            void flushEverything() {
 | 
			
		||||
                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 );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        public:
 | 
			
		||||
            FileRedirect():
 | 
			
		||||
                m_originalOut( dup( fileno( stdout ) ) ),
 | 
			
		||||
                m_originalErr( dup( fileno( stderr ) ) ) {
 | 
			
		||||
                CATCH_ENFORCE( m_originalOut >= 0, "Could not dup stdout" );
 | 
			
		||||
                CATCH_ENFORCE( m_originalErr >= 0, "Could not dup stderr" );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::string getStdout() override { return m_outFile.getContents(); }
 | 
			
		||||
            std::string getStderr() override { return m_errFile.getContents(); }
 | 
			
		||||
            void clearBuffers() override {
 | 
			
		||||
                m_outFile.clear();
 | 
			
		||||
                m_errFile.clear();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void activateImpl() override {
 | 
			
		||||
                // We flush before starting redirect, to ensure that we do
 | 
			
		||||
                // not capture the end of message sent before activation.
 | 
			
		||||
                flushEverything();
 | 
			
		||||
 | 
			
		||||
                int ret;
 | 
			
		||||
                ret = dup2( fileno( m_outFile.getFile() ), fileno( stdout ) );
 | 
			
		||||
                CATCH_ENFORCE( ret >= 0,
 | 
			
		||||
                               "dup2 to stdout has failed, errno: " << errno );
 | 
			
		||||
                ret = dup2( fileno( m_errFile.getFile() ), fileno( stderr ) );
 | 
			
		||||
                CATCH_ENFORCE( ret >= 0,
 | 
			
		||||
                               "dup2 to stderr has failed, errno: " << errno );
 | 
			
		||||
            }
 | 
			
		||||
            void deactivateImpl() override {
 | 
			
		||||
                // We flush before ending redirect, to ensure that we
 | 
			
		||||
                // capture all messages sent while the redirect was active.
 | 
			
		||||
                flushEverything();
 | 
			
		||||
 | 
			
		||||
                int ret;
 | 
			
		||||
                ret = dup2( m_originalOut, fileno( stdout ) );
 | 
			
		||||
                CATCH_ENFORCE(
 | 
			
		||||
                    ret >= 0,
 | 
			
		||||
                    "dup2 of original stdout has failed, errno: " << errno );
 | 
			
		||||
                ret = dup2( m_originalErr, fileno( stderr ) );
 | 
			
		||||
                CATCH_ENFORCE(
 | 
			
		||||
                    ret >= 0,
 | 
			
		||||
                    "dup2 of original stderr has failed, errno: " << errno );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
#endif // CATCH_CONFIG_NEW_CAPTURE
 | 
			
		||||
 | 
			
		||||
    } // end namespace
 | 
			
		||||
 | 
			
		||||
    bool isRedirectAvailable( OutputRedirect::Kind kind ) {
 | 
			
		||||
        switch ( kind ) {
 | 
			
		||||
        // These two are always available
 | 
			
		||||
        case OutputRedirect::None:
 | 
			
		||||
        case OutputRedirect::Streams:
 | 
			
		||||
            return true;
 | 
			
		||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
 | 
			
		||||
        case OutputRedirect::FileDescriptors:
 | 
			
		||||
            return true;
 | 
			
		||||
#endif
 | 
			
		||||
        default:
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Detail::unique_ptr<OutputRedirect> makeOutputRedirect( bool actual ) {
 | 
			
		||||
        if ( actual ) {
 | 
			
		||||
            // TODO: Clean this up later
 | 
			
		||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
 | 
			
		||||
            return Detail::make_unique<FileRedirect>();
 | 
			
		||||
#else
 | 
			
		||||
            return Detail::make_unique<StreamRedirect>();
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            return Detail::make_unique<NoopRedirect>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) {
 | 
			
		||||
        return RedirectGuard( true, redirectImpl );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) {
 | 
			
		||||
        return RedirectGuard( false, redirectImpl );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OutputRedirect::~OutputRedirect() = default;
 | 
			
		||||
 | 
			
		||||
    RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ):
 | 
			
		||||
        m_redirect( &redirectImpl ),
 | 
			
		||||
        m_activate( activate ),
 | 
			
		||||
        m_previouslyActive( redirectImpl.isActive() ) {
 | 
			
		||||
 | 
			
		||||
        // Skip cases where there is no actual state change.
 | 
			
		||||
        if ( m_activate == m_previouslyActive ) { return; }
 | 
			
		||||
 | 
			
		||||
        if ( m_activate ) {
 | 
			
		||||
            m_redirect->activate();
 | 
			
		||||
        } else {
 | 
			
		||||
            m_redirect->deactivate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RedirectGuard::~RedirectGuard() noexcept( false ) {
 | 
			
		||||
        if ( m_moved ) { return; }
 | 
			
		||||
        // Skip cases where there is no actual state change.
 | 
			
		||||
        if ( m_activate == m_previouslyActive ) { return; }
 | 
			
		||||
 | 
			
		||||
        if ( m_activate ) {
 | 
			
		||||
            m_redirect->deactivate();
 | 
			
		||||
        } else {
 | 
			
		||||
            m_redirect->activate();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RedirectGuard::RedirectGuard( RedirectGuard&& rhs ) noexcept:
 | 
			
		||||
        m_redirect( rhs.m_redirect ),
 | 
			
		||||
        m_activate( rhs.m_activate ),
 | 
			
		||||
        m_previouslyActive( rhs.m_previouslyActive ),
 | 
			
		||||
        m_moved( false ) {
 | 
			
		||||
        rhs.m_moved = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RedirectGuard& RedirectGuard::operator=( RedirectGuard&& rhs ) noexcept {
 | 
			
		||||
        m_redirect = rhs.m_redirect;
 | 
			
		||||
        m_activate = rhs.m_activate;
 | 
			
		||||
        m_previouslyActive = rhs.m_previouslyActive;
 | 
			
		||||
        m_moved = false;
 | 
			
		||||
        rhs.m_moved = true;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // namespace Catch
 | 
			
		||||
 | 
			
		||||
#if defined(CATCH_CONFIG_NEW_CAPTURE)
 | 
			
		||||
    #if defined(_MSC_VER)
 | 
			
		||||
    #undef dup
 | 
			
		||||
    #undef dup2
 | 
			
		||||
    #undef fileno
 | 
			
		||||
    #endif
 | 
			
		||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
 | 
			
		||||
#    if defined( _MSC_VER )
 | 
			
		||||
#        undef dup
 | 
			
		||||
#        undef dup2
 | 
			
		||||
#        undef fileno
 | 
			
		||||
#    endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -5573,6 +5770,7 @@ namespace Catch {
 | 
			
		||||
        m_config(_config),
 | 
			
		||||
        m_reporter(CATCH_MOVE(reporter)),
 | 
			
		||||
        m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
 | 
			
		||||
        m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ),
 | 
			
		||||
        m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
 | 
			
		||||
    {
 | 
			
		||||
        getCurrentMutableContext().setResultCapture( this );
 | 
			
		||||
@@ -5588,6 +5786,7 @@ namespace Catch {
 | 
			
		||||
 | 
			
		||||
        auto const& testInfo = testCase.getTestCaseInfo();
 | 
			
		||||
        m_reporter->testCaseStarting(testInfo);
 | 
			
		||||
        testCase.prepareTestCase();
 | 
			
		||||
        m_activeTestCase = &testCase;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -5638,15 +5837,17 @@ namespace Catch {
 | 
			
		||||
            m_reporter->testCasePartialStarting(testInfo, testRuns);
 | 
			
		||||
 | 
			
		||||
            const auto beforeRunTotals = m_totals;
 | 
			
		||||
            std::string oneRunCout, oneRunCerr;
 | 
			
		||||
            runCurrentTest(oneRunCout, oneRunCerr);
 | 
			
		||||
            runCurrentTest();
 | 
			
		||||
            std::string oneRunCout = m_outputRedirect->getStdout();
 | 
			
		||||
            std::string oneRunCerr = m_outputRedirect->getStderr();
 | 
			
		||||
            m_outputRedirect->clearBuffers();
 | 
			
		||||
            redirectedCout += oneRunCout;
 | 
			
		||||
            redirectedCerr += oneRunCerr;
 | 
			
		||||
 | 
			
		||||
            const auto singleRunTotals = m_totals.delta(beforeRunTotals);
 | 
			
		||||
            auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
 | 
			
		||||
 | 
			
		||||
            m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
 | 
			
		||||
 | 
			
		||||
            ++testRuns;
 | 
			
		||||
        } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
 | 
			
		||||
 | 
			
		||||
@@ -5657,6 +5858,7 @@ namespace Catch {
 | 
			
		||||
            deltaTotals.testCases.failed++;
 | 
			
		||||
        }
 | 
			
		||||
        m_totals.testCases += deltaTotals.testCases;
 | 
			
		||||
        testCase.tearDownTestCase();
 | 
			
		||||
        m_reporter->testCaseEnded(TestCaseStats(testInfo,
 | 
			
		||||
                                  deltaTotals,
 | 
			
		||||
                                  CATCH_MOVE(redirectedCout),
 | 
			
		||||
@@ -5690,7 +5892,10 @@ namespace Catch {
 | 
			
		||||
            m_lastAssertionPassed = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
 | 
			
		||||
        {
 | 
			
		||||
            auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
            m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( result.getResultType() != ResultWas::Warning ) {
 | 
			
		||||
            m_messageScopes.clear();
 | 
			
		||||
@@ -5707,6 +5912,7 @@ namespace Catch {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
 | 
			
		||||
        auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
        m_reporter->assertionStarting( info );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -5725,7 +5931,10 @@ namespace Catch {
 | 
			
		||||
        SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
 | 
			
		||||
        m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
 | 
			
		||||
 | 
			
		||||
        m_reporter->sectionStarting(sectionInfo);
 | 
			
		||||
        {
 | 
			
		||||
            auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
            m_reporter->sectionStarting( sectionInfo );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assertions = m_totals.assertions;
 | 
			
		||||
 | 
			
		||||
@@ -5785,7 +5994,15 @@ namespace Catch {
 | 
			
		||||
            m_activeSections.pop_back();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
 | 
			
		||||
        {
 | 
			
		||||
            auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
            m_reporter->sectionEnded(
 | 
			
		||||
                SectionStats( CATCH_MOVE( endInfo.sectionInfo ),
 | 
			
		||||
                              assertions,
 | 
			
		||||
                              endInfo.durationInSeconds,
 | 
			
		||||
                              missingAssertions ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        m_messages.clear();
 | 
			
		||||
        m_messageScopes.clear();
 | 
			
		||||
    }
 | 
			
		||||
@@ -5802,15 +6019,19 @@ namespace Catch {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RunContext::benchmarkPreparing( StringRef name ) {
 | 
			
		||||
        m_reporter->benchmarkPreparing(name);
 | 
			
		||||
        auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
        m_reporter->benchmarkPreparing( name );
 | 
			
		||||
    }
 | 
			
		||||
    void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
 | 
			
		||||
        auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
        m_reporter->benchmarkStarting( info );
 | 
			
		||||
    }
 | 
			
		||||
    void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
 | 
			
		||||
        auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
        m_reporter->benchmarkEnded( stats );
 | 
			
		||||
    }
 | 
			
		||||
    void RunContext::benchmarkFailed( StringRef error ) {
 | 
			
		||||
        auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
        m_reporter->benchmarkFailed( error );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -5841,8 +6062,13 @@ namespace Catch {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RunContext::handleFatalErrorCondition( StringRef message ) {
 | 
			
		||||
        // TODO: scoped deactivate here? Just give up and do best effort?
 | 
			
		||||
        //       the deactivation can break things further, OTOH so can the
 | 
			
		||||
        //       capture
 | 
			
		||||
        auto _ = scopedDeactivate( *m_outputRedirect );
 | 
			
		||||
 | 
			
		||||
        // First notify reporter that bad things happened
 | 
			
		||||
        m_reporter->fatalErrorEncountered(message);
 | 
			
		||||
        m_reporter->fatalErrorEncountered( message );
 | 
			
		||||
 | 
			
		||||
        // Don't rebuild the result -- the stringification itself can cause more fatal errors
 | 
			
		||||
        // Instead, fake a result data.
 | 
			
		||||
@@ -5869,7 +6095,7 @@ namespace Catch {
 | 
			
		||||
        Counts assertions;
 | 
			
		||||
        assertions.failed = 1;
 | 
			
		||||
        SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
 | 
			
		||||
        m_reporter->sectionEnded(testCaseSectionStats);
 | 
			
		||||
        m_reporter->sectionEnded( testCaseSectionStats );
 | 
			
		||||
 | 
			
		||||
        auto const& testInfo = m_activeTestCase->getTestCaseInfo();
 | 
			
		||||
 | 
			
		||||
@@ -5900,7 +6126,7 @@ namespace Catch {
 | 
			
		||||
        return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
 | 
			
		||||
    void RunContext::runCurrentTest() {
 | 
			
		||||
        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
 | 
			
		||||
        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
 | 
			
		||||
        m_reporter->sectionStarting(testCaseSection);
 | 
			
		||||
@@ -5911,18 +6137,8 @@ namespace Catch {
 | 
			
		||||
 | 
			
		||||
        Timer timer;
 | 
			
		||||
        CATCH_TRY {
 | 
			
		||||
            if (m_reporter->getPreferences().shouldRedirectStdOut) {
 | 
			
		||||
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
 | 
			
		||||
                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
 | 
			
		||||
 | 
			
		||||
                timer.start();
 | 
			
		||||
                invokeActiveTestCase();
 | 
			
		||||
#else
 | 
			
		||||
                OutputRedirect r(redirectedCout, redirectedCerr);
 | 
			
		||||
                timer.start();
 | 
			
		||||
                invokeActiveTestCase();
 | 
			
		||||
#endif
 | 
			
		||||
            } else {
 | 
			
		||||
            {
 | 
			
		||||
                auto _ = scopedActivate( *m_outputRedirect );
 | 
			
		||||
                timer.start();
 | 
			
		||||
                invokeActiveTestCase();
 | 
			
		||||
            }
 | 
			
		||||
@@ -5967,11 +6183,12 @@ namespace Catch {
 | 
			
		||||
    void RunContext::handleUnfinishedSections() {
 | 
			
		||||
        // If sections ended prematurely due to an exception we stored their
 | 
			
		||||
        // infos here so we can tear them down outside the unwind process.
 | 
			
		||||
        for (auto it = m_unfinishedSections.rbegin(),
 | 
			
		||||
             itEnd = m_unfinishedSections.rend();
 | 
			
		||||
             it != itEnd;
 | 
			
		||||
             ++it)
 | 
			
		||||
            sectionEnded(CATCH_MOVE(*it));
 | 
			
		||||
        for ( auto it = m_unfinishedSections.rbegin(),
 | 
			
		||||
                   itEnd = m_unfinishedSections.rend();
 | 
			
		||||
              it != itEnd;
 | 
			
		||||
              ++it ) {
 | 
			
		||||
            sectionEnded( CATCH_MOVE( *it ) );
 | 
			
		||||
        }
 | 
			
		||||
        m_unfinishedSections.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -6898,6 +7115,8 @@ namespace Catch {
 | 
			
		||||
#include <iterator>
 | 
			
		||||
 | 
			
		||||
namespace Catch {
 | 
			
		||||
    void ITestInvoker::prepareTestCase() {}
 | 
			
		||||
    void ITestInvoker::tearDownTestCase() {}
 | 
			
		||||
    ITestInvoker::~ITestInvoker() = default;
 | 
			
		||||
 | 
			
		||||
    namespace {
 | 
			
		||||
@@ -10333,7 +10552,7 @@ namespace Catch {
 | 
			
		||||
            xml( m_stream )
 | 
			
		||||
        {
 | 
			
		||||
            m_preferences.shouldRedirectStdOut = true;
 | 
			
		||||
            m_preferences.shouldReportAllAssertions = true;
 | 
			
		||||
            m_preferences.shouldReportAllAssertions = false;
 | 
			
		||||
            m_shouldStoreSuccesfulAssertions = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -10443,7 +10662,7 @@ namespace Catch {
 | 
			
		||||
        if( !rootName.empty() )
 | 
			
		||||
            name = rootName + '/' + name;
 | 
			
		||||
 | 
			
		||||
        if( sectionNode.hasAnyAssertions()
 | 
			
		||||
        if ( sectionNode.stats.assertions.total() > 0
 | 
			
		||||
           || !sectionNode.stdOut.empty()
 | 
			
		||||
           || !sectionNode.stdErr.empty() ) {
 | 
			
		||||
            XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user