mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 04:07:10 +01:00 
			
		
		
		
	v3.7.0
This commit is contained in:
		| @@ -33,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) | ||||
| endif() | ||||
|  | ||||
| project(Catch2 | ||||
|   VERSION 3.6.0 # CML version placeholder, don't delete | ||||
|   VERSION 3.7.0 # CML version placeholder, don't delete | ||||
|   LANGUAGES CXX | ||||
|   # HOMEPAGE_URL is not supported until CMake version 3.12, which | ||||
|   # we do not target yet. | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| # Release notes | ||||
| **Contents**<br> | ||||
| [3.7.0](#370)<br> | ||||
| [3.6.0](#360)<br> | ||||
| [3.5.4](#354)<br> | ||||
| [3.5.3](#353)<br> | ||||
| @@ -63,6 +64,26 @@ | ||||
| [Even Older versions](#even-older-versions)<br> | ||||
|  | ||||
|  | ||||
| ## 3.7.0 | ||||
|  | ||||
| ### improvements | ||||
| * Slightly improved compile times of benchmarks | ||||
| * Made the resolution estimation in benchmarks slightly more precise | ||||
| * Added new test case macro, `TEST_CASE_PERSISTENT_FIXTURE` (#2885, #1602) | ||||
|   * Unlike `TEST_CASE_METHOD`, the same underlying instance is used for all partial runs of that test case | ||||
| * **MASSIVELY** improved performance of the JUnit reporter when handling successful assertions (#2897) | ||||
|   * For 1 test case and 10M assertions, the new reporter runs 3x faster and uses up only 8 MB of memory, while the old one needs 7 GB of memory. | ||||
| * Reworked how output redirects works. | ||||
|   * Combining a reporter writing to stdout with capturing reporter no longer leads to the capturing reporter seeing all of the other reporter's output. | ||||
|   * The file based redirect no longer opens up a new temporary file for each partial test case run, so it will not run out of temporary files when running many tests in single process. | ||||
|  | ||||
| ### Miscellaneous | ||||
| * Better documentation for matchers on thrown exceptions (`REQUIRE_THROWS_MATCHES`) | ||||
| * Improved `catch_discover_tests`'s handling of environment paths (#2878) | ||||
|   * It won't reorder paths in `DL_PATHS` or `DYLD_FRAMEWORK_PATHS` args | ||||
|   * It won't overwrite the environment paths for test discovery | ||||
|  | ||||
|  | ||||
| ## 3.6.0 | ||||
|  | ||||
| ### Fixes | ||||
|   | ||||
| @@ -88,7 +88,7 @@ class. | ||||
|  | ||||
| ### 3. `TEST_CASE_PERSISTENT_FIXTURE` | ||||
|  | ||||
| > [Introduced](https://github.com/catchorg/Catch2/pull/2885) in Catch2 X.Y.Z | ||||
| > [Introduced](https://github.com/catchorg/Catch2/pull/2885) in Catch2 3.7.0 | ||||
|  | ||||
| `TEST_CASE_PERSISTENT_FIXTURE` behaves in the same way as | ||||
| [TEST_CASE_METHOD](#1-test_case_method) except that there will only be | ||||
|   | ||||
| @@ -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" ); | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
|  | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| //  Catch v3.6.0 | ||||
| //  Generated: 2024-05-05 20:53:27.071502 | ||||
| //  Catch v3.7.0 | ||||
| //  Generated: 2024-08-14 12:04:53.220567 | ||||
| //  ---------------------------------------------------------- | ||||
| //  This file is an amalgamation of multiple different files. | ||||
| //  You probably shouldn't edit it directly. | ||||
| @@ -1584,22 +1584,17 @@ namespace Catch { | ||||
|             private: | ||||
|                 struct callable { | ||||
|                     virtual void call(Chronometer meter) const = 0; | ||||
|                     virtual Catch::Detail::unique_ptr<callable> clone() const = 0; | ||||
|                     virtual ~callable(); // = default; | ||||
|  | ||||
|                     callable() = default; | ||||
|                     callable(callable const&) = default; | ||||
|                     callable& operator=(callable const&) = default; | ||||
|                     callable(callable&&) = default; | ||||
|                     callable& operator=(callable&&) = default; | ||||
|                 }; | ||||
|                 template <typename Fun> | ||||
|                 struct model : public callable { | ||||
|                     model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} | ||||
|                     model(Fun const& fun_) : fun(fun_) {} | ||||
|  | ||||
|                     Catch::Detail::unique_ptr<callable> clone() const override { | ||||
|                         return Catch::Detail::make_unique<model<Fun>>( *this ); | ||||
|                     } | ||||
|  | ||||
|                     void call(Chronometer meter) const override { | ||||
|                         call(meter, is_callable<Fun(Chronometer)>()); | ||||
|                     } | ||||
| @@ -1613,14 +1608,8 @@ namespace Catch { | ||||
|                     Fun fun; | ||||
|                 }; | ||||
|  | ||||
|                 struct do_nothing { void operator()() const {} }; | ||||
|  | ||||
|                 template <typename T> | ||||
|                 BenchmarkFunction(model<T>* c) : f(c) {} | ||||
|  | ||||
|             public: | ||||
|                 BenchmarkFunction() | ||||
|                     : f(new model<do_nothing>{ {} }) {} | ||||
|                 BenchmarkFunction(); | ||||
|  | ||||
|                 template <typename Fun, | ||||
|                     std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0> | ||||
| @@ -1630,20 +1619,12 @@ namespace Catch { | ||||
|                 BenchmarkFunction( BenchmarkFunction&& that ) noexcept: | ||||
|                     f( CATCH_MOVE( that.f ) ) {} | ||||
|  | ||||
|                 BenchmarkFunction(BenchmarkFunction const& that) | ||||
|                     : f(that.f->clone()) {} | ||||
|  | ||||
|                 BenchmarkFunction& | ||||
|                 operator=( BenchmarkFunction&& that ) noexcept { | ||||
|                     f = CATCH_MOVE( that.f ); | ||||
|                     return *this; | ||||
|                 } | ||||
|  | ||||
|                 BenchmarkFunction& operator=(BenchmarkFunction const& that) { | ||||
|                     f = that.f->clone(); | ||||
|                     return *this; | ||||
|                 } | ||||
|  | ||||
|                 void operator()(Chronometer meter) const { f->call(meter); } | ||||
|  | ||||
|             private: | ||||
| @@ -1780,7 +1761,7 @@ namespace Catch { | ||||
|             template <typename Clock, typename Fun, typename... Args> | ||||
|             TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) { | ||||
|                 auto start = Clock::now(); | ||||
|                 auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); | ||||
|                 auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); | ||||
|                 auto end = Clock::now(); | ||||
|                 auto delta = end - start; | ||||
|                 return { delta, CATCH_FORWARD(r), 1 }; | ||||
| @@ -1946,15 +1927,17 @@ namespace Catch { | ||||
|         namespace Detail { | ||||
|             template <typename Clock> | ||||
|             std::vector<double> resolution(int k) { | ||||
|                 std::vector<TimePoint<Clock>> times; | ||||
|                 times.reserve(static_cast<size_t>(k + 1)); | ||||
|                 for ( int i = 0; i < k + 1; ++i ) { | ||||
|                     times.push_back( Clock::now() ); | ||||
|                 const size_t points = static_cast<size_t>( k + 1 ); | ||||
|                 // To avoid overhead from the branch inside vector::push_back, | ||||
|                 // we allocate them all and then overwrite. | ||||
|                 std::vector<TimePoint<Clock>> times(points); | ||||
|                 for ( auto& time : times ) { | ||||
|                     time = Clock::now(); | ||||
|                 } | ||||
|  | ||||
|                 std::vector<double> deltas; | ||||
|                 deltas.reserve(static_cast<size_t>(k)); | ||||
|                 for ( size_t idx = 1; idx < times.size(); ++idx ) { | ||||
|                 for ( size_t idx = 1; idx < points; ++idx ) { | ||||
|                     deltas.push_back( static_cast<double>( | ||||
|                         ( times[idx] - times[idx - 1] ).count() ) ); | ||||
|                 } | ||||
| @@ -2103,12 +2086,12 @@ namespace Catch { | ||||
|                 : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} | ||||
|  | ||||
|             template <typename Clock> | ||||
|             ExecutionPlan prepare(const IConfig &cfg, Environment env) const { | ||||
|             ExecutionPlan prepare(const IConfig &cfg, Environment env) { | ||||
|                 auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; | ||||
|                 auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime())); | ||||
|                 auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun); | ||||
|                 int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed)); | ||||
|                 return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; | ||||
|                 return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; | ||||
|             } | ||||
|  | ||||
|             template <typename Clock = default_clock> | ||||
| @@ -3347,6 +3330,18 @@ namespace Catch { | ||||
| #endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED | ||||
|  | ||||
|  | ||||
| #ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED | ||||
| #define CATCH_CASE_SENSITIVE_HPP_INCLUDED | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     enum class CaseSensitive { Yes, No }; | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
| #endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED | ||||
|  | ||||
|  | ||||
| #ifndef CATCH_CONFIG_HPP_INCLUDED | ||||
| #define CATCH_CONFIG_HPP_INCLUDED | ||||
|  | ||||
| @@ -3366,18 +3361,6 @@ namespace Catch { | ||||
| #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED | ||||
|  | ||||
|  | ||||
|  | ||||
| #ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED | ||||
| #define CATCH_CASE_SENSITIVE_HPP_INCLUDED | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     enum class CaseSensitive { Yes, No }; | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
| #endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| namespace Catch | ||||
| @@ -5953,6 +5936,8 @@ namespace Catch { | ||||
|  | ||||
|     class ITestInvoker { | ||||
|     public: | ||||
|         virtual void prepareTestCase(); | ||||
|         virtual void tearDownTestCase(); | ||||
|         virtual void invoke() const = 0; | ||||
|         virtual ~ITestInvoker(); // = default | ||||
|     }; | ||||
| @@ -6005,6 +5990,33 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) { | ||||
|     return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod ); | ||||
| } | ||||
|  | ||||
| template <typename C> | ||||
| class TestInvokerFixture : public ITestInvoker { | ||||
|     void ( C::*m_testAsMethod )() const; | ||||
|     Detail::unique_ptr<C> m_fixture = nullptr; | ||||
|  | ||||
| public: | ||||
|     TestInvokerFixture( void ( C::*testAsMethod )() const) noexcept : m_testAsMethod( testAsMethod ) {} | ||||
|  | ||||
|     void prepareTestCase() override { | ||||
|         m_fixture = Detail::make_unique<C>(); | ||||
|     } | ||||
|  | ||||
|     void tearDownTestCase() override { | ||||
|         m_fixture.reset(); | ||||
|     } | ||||
|  | ||||
|     void invoke() const override { | ||||
|         auto* f = m_fixture.get(); | ||||
|         ( f->*m_testAsMethod )(); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template<typename C> | ||||
| Detail::unique_ptr<ITestInvoker> makeTestInvokerFixture( void ( C::*testAsMethod )() const ) { | ||||
|     return Detail::make_unique<TestInvokerFixture<C>>( testAsMethod ); | ||||
| } | ||||
|  | ||||
| struct NameAndTags { | ||||
|     constexpr NameAndTags( StringRef name_ = StringRef(), | ||||
|                            StringRef tags_ = StringRef() ) noexcept: | ||||
| @@ -6101,6 +6113,26 @@ static int catchInternalSectionHint = 0; | ||||
|     #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ | ||||
|         INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////////// | ||||
|     #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... )      \ | ||||
|         CATCH_INTERNAL_START_WARNINGS_SUPPRESSION                             \ | ||||
|         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                              \ | ||||
|         CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS                      \ | ||||
|         namespace {                                                           \ | ||||
|             struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) {     \ | ||||
|                 void test() const;                                            \ | ||||
|             };                                                                \ | ||||
|             const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ | ||||
|                 Catch::makeTestInvokerFixture( &TestName::test ),                    \ | ||||
|                 CATCH_INTERNAL_LINEINFO,                                      \ | ||||
|                 #ClassName##_catch_sr,                                        \ | ||||
|                 Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */             \ | ||||
|         }                                                                     \ | ||||
|         CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                              \ | ||||
|         void TestName::test() const | ||||
|     #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... )    \ | ||||
|         INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) | ||||
|  | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////////// | ||||
|     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ | ||||
| @@ -6158,6 +6190,7 @@ static int catchInternalSectionHint = 0; | ||||
|   #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) | ||||
|   #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) | ||||
|   #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) | ||||
|   #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) | ||||
|   #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) | ||||
|   #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) | ||||
|   #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) | ||||
| @@ -6212,6 +6245,7 @@ static int catchInternalSectionHint = 0; | ||||
|   #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) | ||||
|   #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) | ||||
|   #define CATCH_METHOD_AS_TEST_CASE( method, ... ) | ||||
|   #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) | ||||
|   #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) | ||||
|   #define CATCH_SECTION( ... ) | ||||
|   #define CATCH_DYNAMIC_SECTION( ... ) | ||||
| @@ -6257,6 +6291,7 @@ static int catchInternalSectionHint = 0; | ||||
|   #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) | ||||
|   #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) | ||||
|   #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) | ||||
|   #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) | ||||
|   #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) | ||||
|   #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) | ||||
|   #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) | ||||
| @@ -6310,6 +6345,7 @@ static int catchInternalSectionHint = 0; | ||||
|   #define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) | ||||
|   #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) | ||||
|   #define METHOD_AS_TEST_CASE( method, ... ) | ||||
|   #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) | ||||
|   #define REGISTER_TEST_CASE( Function, ... ) (void)(0) | ||||
|   #define SECTION( ... ) | ||||
|   #define DYNAMIC_SECTION( ... ) | ||||
| @@ -7103,6 +7139,14 @@ namespace Catch { | ||||
|         TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : | ||||
|             m_info(info), m_invoker(invoker) {} | ||||
|  | ||||
|         void prepareTestCase() const { | ||||
|             m_invoker->prepareTestCase(); | ||||
|         } | ||||
|  | ||||
|         void tearDownTestCase() const { | ||||
|             m_invoker->tearDownTestCase(); | ||||
|         } | ||||
|  | ||||
|         void invoke() const { | ||||
|             m_invoker->invoke(); | ||||
|         } | ||||
| @@ -7271,7 +7315,7 @@ namespace Catch { | ||||
| #define CATCH_VERSION_MACROS_HPP_INCLUDED | ||||
|  | ||||
| #define CATCH_VERSION_MAJOR 3 | ||||
| #define CATCH_VERSION_MINOR 6 | ||||
| #define CATCH_VERSION_MINOR 7 | ||||
| #define CATCH_VERSION_PATCH 0 | ||||
|  | ||||
| #endif // CATCH_VERSION_MACROS_HPP_INCLUDED | ||||
| @@ -10033,106 +10077,67 @@ namespace Catch { | ||||
| #define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED | ||||
|  | ||||
|  | ||||
| #include <cstdio> | ||||
| #include <iosfwd> | ||||
| #include <cassert> | ||||
| #include <string> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     class RedirectedStream { | ||||
|         std::ostream& m_originalStream; | ||||
|         std::ostream& m_redirectionStream; | ||||
|         std::streambuf* m_prevBuf; | ||||
|  | ||||
|     public: | ||||
|         RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); | ||||
|         ~RedirectedStream(); | ||||
|     }; | ||||
|  | ||||
|     class RedirectedStdOut { | ||||
|         ReusableStringStream m_rss; | ||||
|         RedirectedStream m_cout; | ||||
|     public: | ||||
|         RedirectedStdOut(); | ||||
|         auto str() const -> std::string; | ||||
|     }; | ||||
|  | ||||
|     // StdErr has two constituent streams in C++, std::cerr and std::clog | ||||
|     // This means that we need to redirect 2 streams into 1 to keep proper | ||||
|     // order of writes | ||||
|     class RedirectedStdErr { | ||||
|         ReusableStringStream m_rss; | ||||
|         RedirectedStream m_cerr; | ||||
|         RedirectedStream m_clog; | ||||
|     public: | ||||
|         RedirectedStdErr(); | ||||
|         auto str() const -> std::string; | ||||
|     }; | ||||
|  | ||||
|     class RedirectedStreams { | ||||
|     public: | ||||
|         RedirectedStreams(RedirectedStreams const&) = delete; | ||||
|         RedirectedStreams& operator=(RedirectedStreams const&) = delete; | ||||
|         RedirectedStreams(RedirectedStreams&&) = delete; | ||||
|         RedirectedStreams& operator=(RedirectedStreams&&) = delete; | ||||
|  | ||||
|         RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); | ||||
|         ~RedirectedStreams(); | ||||
|     private: | ||||
|         std::string& m_redirectedCout; | ||||
|         std::string& m_redirectedCerr; | ||||
|         RedirectedStdOut m_redirectedStdOut; | ||||
|         RedirectedStdErr m_redirectedStdErr; | ||||
|     }; | ||||
|  | ||||
| #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 { | ||||
|         bool m_redirectActive = false; | ||||
|         virtual void activateImpl() = 0; | ||||
|         virtual void deactivateImpl() = 0; | ||||
|     public: | ||||
|         OutputRedirect(OutputRedirect const&) = delete; | ||||
|         OutputRedirect& operator=(OutputRedirect const&) = delete; | ||||
|         OutputRedirect(OutputRedirect&&) = delete; | ||||
|         OutputRedirect& operator=(OutputRedirect&&) = delete; | ||||
|         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 ~OutputRedirect(); // = default; | ||||
|  | ||||
|         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; | ||||
|         // TODO: Do we want to check that redirect is not active before retrieving the output? | ||||
|         virtual std::string getStdout() = 0; | ||||
|         virtual std::string getStderr() = 0; | ||||
|         virtual void clearBuffers() = 0; | ||||
|         bool isActive() const { return m_redirectActive; } | ||||
|         void activate() { | ||||
|             assert( !m_redirectActive && "redirect is already active" ); | ||||
|             activateImpl(); | ||||
|             m_redirectActive = true; | ||||
|         } | ||||
|         void deactivate() { | ||||
|             assert( m_redirectActive && "redirect is not active" ); | ||||
|             deactivateImpl(); | ||||
|             m_redirectActive = false; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| #endif | ||||
|     bool isRedirectAvailable( OutputRedirect::Kind kind); | ||||
|     Detail::unique_ptr<OutputRedirect> makeOutputRedirect( bool actual ); | ||||
|  | ||||
|     class RedirectGuard { | ||||
|         OutputRedirect* m_redirect; | ||||
|         bool m_activate; | ||||
|         bool m_previouslyActive; | ||||
|         bool m_moved = false; | ||||
|  | ||||
|     public: | ||||
|         RedirectGuard( bool activate, OutputRedirect& redirectImpl ); | ||||
|         ~RedirectGuard() noexcept( false ); | ||||
|  | ||||
|         RedirectGuard( RedirectGuard const& ) = delete; | ||||
|         RedirectGuard& operator=( RedirectGuard const& ) = delete; | ||||
|  | ||||
|         // C++14 needs move-able guards to return them from functions | ||||
|         RedirectGuard( RedirectGuard&& rhs ) noexcept; | ||||
|         RedirectGuard& operator=( RedirectGuard&& rhs ) noexcept; | ||||
|     }; | ||||
|  | ||||
|     RedirectGuard scopedActivate( OutputRedirect& redirectImpl ); | ||||
|     RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ); | ||||
|  | ||||
| } // end namespace Catch | ||||
|  | ||||
| @@ -10455,6 +10460,7 @@ namespace Catch { | ||||
|     class IConfig; | ||||
|     class IEventListener; | ||||
|     using IEventListenerPtr = Detail::unique_ptr<IEventListener>; | ||||
|     class OutputRedirect; | ||||
|  | ||||
|     /////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| @@ -10541,7 +10547,7 @@ namespace Catch { | ||||
|  | ||||
|     private: | ||||
|  | ||||
|         void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); | ||||
|         void runCurrentTest(); | ||||
|         void invokeActiveTestCase(); | ||||
|  | ||||
|         void resetAssertionInfo(); | ||||
| @@ -10574,6 +10580,7 @@ namespace Catch { | ||||
|         std::vector<SectionEndInfo> m_unfinishedSections; | ||||
|         std::vector<ITracker*> m_activeSections; | ||||
|         TrackerContext m_trackerContext; | ||||
|         Detail::unique_ptr<OutputRedirect> m_outputRedirect; | ||||
|         FatalConditionHandler m_fatalConditionhandler; | ||||
|         bool m_lastAssertionPassed = false; | ||||
|         bool m_shouldReportUnexpected = true; | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| project( | ||||
|   'catch2', | ||||
|   'cpp', | ||||
|   version: '3.6.0', # CML version placeholder, don't delete | ||||
|   version: '3.7.0', # CML version placeholder, don't delete | ||||
|   license: 'BSL-1.0', | ||||
|   meson_version: '>=0.54.1', | ||||
| ) | ||||
|   | ||||
| @@ -112,12 +112,12 @@ namespace Catch { | ||||
|         TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : | ||||
|             m_info(info), m_invoker(invoker) {} | ||||
|  | ||||
|         void prepareTestCase() const {  | ||||
|         void prepareTestCase() const { | ||||
|             m_invoker->prepareTestCase(); | ||||
|         } | ||||
|  | ||||
|         void tearDownTestCase() const {  | ||||
|             m_invoker->tearDownTestCase();  | ||||
|         void tearDownTestCase() const { | ||||
|             m_invoker->tearDownTestCase(); | ||||
|         } | ||||
|  | ||||
|         void invoke() const { | ||||
|   | ||||
| @@ -36,7 +36,7 @@ namespace Catch { | ||||
|     } | ||||
|  | ||||
|     Version const& libraryVersion() { | ||||
|         static Version version( 3, 6, 0, "", 0 ); | ||||
|         static Version version( 3, 7, 0, "", 0 ); | ||||
|         return version; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #define CATCH_VERSION_MACROS_HPP_INCLUDED | ||||
|  | ||||
| #define CATCH_VERSION_MAJOR 3 | ||||
| #define CATCH_VERSION_MINOR 6 | ||||
| #define CATCH_VERSION_MINOR 7 | ||||
| #define CATCH_VERSION_PATCH 0 | ||||
|  | ||||
| #endif // CATCH_VERSION_MACROS_HPP_INCLUDED | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský