mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-10 15:35:39 +02:00
Compare commits
3 Commits
v3.9.1
...
devel-pipe
Author | SHA1 | Date | |
---|---|---|---|
![]() |
432695291a | ||
![]() |
e63f3cc817 | ||
![]() |
986ee2c793 |
@@ -17,12 +17,19 @@
|
||||
#include <iosfwd>
|
||||
#include <sstream>
|
||||
|
||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
|
||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
#if defined( CATCH_CONFIG_NEW_CAPTURE ) || defined(CATCH_CONFIG_USE_ASYNC)
|
||||
# if defined( _MSC_VER )
|
||||
# include <io.h> //_dup and _dup2
|
||||
# include <fcntl.h> // _O_BINARY
|
||||
# define dup _dup
|
||||
# define dup2 _dup2
|
||||
# define fileno _fileno
|
||||
# define close _close
|
||||
# else
|
||||
# include <unistd.h> // dup and dup2
|
||||
# endif
|
||||
@@ -48,13 +55,13 @@ namespace Catch {
|
||||
* that the underlying stream's rdbuf aren't changed by other
|
||||
* users.
|
||||
*/
|
||||
class RedirectedStreamNew {
|
||||
class RedirectedStream {
|
||||
std::ostream& m_originalStream;
|
||||
std::ostream& m_redirectionStream;
|
||||
std::streambuf* m_prevBuf;
|
||||
|
||||
public:
|
||||
RedirectedStreamNew( std::ostream& originalStream,
|
||||
RedirectedStream( std::ostream& originalStream,
|
||||
std::ostream& redirectionStream ):
|
||||
m_originalStream( originalStream ),
|
||||
m_redirectionStream( redirectionStream ),
|
||||
@@ -72,7 +79,7 @@ namespace Catch {
|
||||
*/
|
||||
class StreamRedirect : public OutputRedirect {
|
||||
ReusableStringStream m_redirectedOut, m_redirectedErr;
|
||||
RedirectedStreamNew m_cout, m_cerr, m_clog;
|
||||
RedirectedStream m_cout, m_cerr, m_clog;
|
||||
|
||||
public:
|
||||
StreamRedirect():
|
||||
@@ -244,6 +251,182 @@ namespace Catch {
|
||||
|
||||
#endif // CATCH_CONFIG_NEW_CAPTURE
|
||||
|
||||
|
||||
#if defined( CATCH_CONFIG_USE_ASYNC )
|
||||
|
||||
static inline void pipe_or_throw( int descriptors[2] ) {
|
||||
# if defined( _MSC_VER )
|
||||
constexpr int defaultPipeSize{ 0 };
|
||||
|
||||
int result{ _pipe( descriptors, defaultPipeSize, _O_BINARY ) };
|
||||
# else
|
||||
int result{ pipe( descriptors ) };
|
||||
# endif
|
||||
|
||||
if ( result ) {
|
||||
CATCH_INTERNAL_ERROR( "pipe-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
read_or_throw( int descriptor, void* buffer, size_t size ) {
|
||||
# if defined( _MSC_VER )
|
||||
int result{
|
||||
_read( descriptor, buffer, static_cast<unsigned>( size ) ) };
|
||||
# else
|
||||
ssize_t result{ read( descriptor, buffer, size ) };
|
||||
# endif
|
||||
|
||||
if ( result == -1 ) {
|
||||
CATCH_INTERNAL_ERROR( "read-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
|
||||
return static_cast<size_t>( result );
|
||||
}
|
||||
|
||||
static inline void close_or_throw( int descriptor ) {
|
||||
if ( close( descriptor ) ) {
|
||||
CATCH_INTERNAL_ERROR( "close-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
}
|
||||
|
||||
static inline int dup_or_throw( int descriptor ) {
|
||||
int result{ dup( descriptor ) };
|
||||
|
||||
if ( result == -1 ) {
|
||||
CATCH_INTERNAL_ERROR( "dup-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int dup2_or_throw( int sourceDescriptor,
|
||||
int destinationDescriptor ) {
|
||||
int result{ dup2( sourceDescriptor, destinationDescriptor ) };
|
||||
|
||||
if ( result == -1 ) {
|
||||
CATCH_INTERNAL_ERROR( "dup2-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int fileno_or_throw( std::FILE* file ) {
|
||||
int result{ fileno( file ) };
|
||||
|
||||
if ( result == -1 ) {
|
||||
CATCH_INTERNAL_ERROR( "fileno-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void fflush_or_throw( std::FILE* file ) {
|
||||
if ( std::fflush( file ) ) {
|
||||
CATCH_INTERNAL_ERROR( "fflush-or-throw" );
|
||||
// CATCH_SYSTEM_ERROR( errno, std::generic_category() );
|
||||
}
|
||||
}
|
||||
|
||||
class StreamPipeHandler {
|
||||
int m_originalFd = -1;
|
||||
int m_pipeReadEnd = -1;
|
||||
int m_pipeWriteEnd = -1;
|
||||
FILE* m_targetStream;
|
||||
std::mutex m_mutex;
|
||||
std::string m_captured;
|
||||
std::thread m_readThread;
|
||||
|
||||
public:
|
||||
StreamPipeHandler( FILE* original ):
|
||||
m_originalFd( dup_or_throw( fileno( original ) ) ),
|
||||
m_targetStream(original)
|
||||
{
|
||||
CATCH_ENFORCE( m_originalFd >= 0, "Could not dup stream" );
|
||||
int pipe_fds[2];
|
||||
pipe_or_throw( pipe_fds );
|
||||
m_pipeReadEnd = pipe_fds[0];
|
||||
m_pipeWriteEnd = pipe_fds[1];
|
||||
|
||||
m_readThread = std::thread([this]() {
|
||||
constexpr size_t bufferSize = 4096;
|
||||
char buffer[bufferSize];
|
||||
size_t sizeRead;
|
||||
while ( ( sizeRead = read_or_throw(
|
||||
m_pipeReadEnd, buffer, bufferSize ) ) != 0 ) {
|
||||
std::unique_lock<std::mutex> _( m_mutex );
|
||||
m_captured.append( buffer, sizeRead );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~StreamPipeHandler() {
|
||||
close_or_throw( m_pipeWriteEnd );
|
||||
m_readThread.join();
|
||||
}
|
||||
|
||||
std::string getCapturedOutput() {
|
||||
std::unique_lock<std::mutex> _( m_mutex );
|
||||
return m_captured;
|
||||
}
|
||||
|
||||
void clearCapturedOutput() {
|
||||
std::unique_lock<std::mutex> _( m_mutex );
|
||||
m_captured.clear();
|
||||
}
|
||||
|
||||
void startCapture() {
|
||||
fflush_or_throw( m_targetStream );
|
||||
int ret = dup2_or_throw( m_pipeWriteEnd, fileno( m_targetStream ) );
|
||||
CATCH_ENFORCE( ret >= 0,
|
||||
"dup2 pipe-write -> original stream failed " << errno );
|
||||
}
|
||||
void stopCapture() {
|
||||
fflush_or_throw( m_targetStream );
|
||||
int ret = dup2_or_throw( m_originalFd, fileno( m_targetStream ) );
|
||||
CATCH_ENFORCE( ret >= 0,
|
||||
"dup2 of original fd -> original stream failed " << errno );
|
||||
}
|
||||
};
|
||||
|
||||
class PipeRedirect : public OutputRedirect {
|
||||
private:
|
||||
StreamPipeHandler m_stdout;
|
||||
StreamPipeHandler m_stderr;
|
||||
public:
|
||||
PipeRedirect():
|
||||
m_stdout(stdout),
|
||||
m_stderr(stderr){}
|
||||
|
||||
void activateImpl() override {
|
||||
m_stdout.startCapture();
|
||||
m_stderr.startCapture();
|
||||
}
|
||||
void deactivateImpl() override {
|
||||
m_stdout.stopCapture();
|
||||
m_stderr.stopCapture();
|
||||
}
|
||||
std::string getStdout() override {
|
||||
return m_stdout.getCapturedOutput();
|
||||
}
|
||||
std::string getStderr() override {
|
||||
return m_stderr.getCapturedOutput();
|
||||
}
|
||||
void clearBuffers() override {
|
||||
m_stdout.clearCapturedOutput();
|
||||
m_stderr.clearCapturedOutput();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CATCH_CONFIG_USE_ASYNC
|
||||
|
||||
|
||||
} // end namespace
|
||||
|
||||
bool isRedirectAvailable( OutputRedirect::Kind kind ) {
|
||||
@@ -255,6 +438,10 @@ namespace Catch {
|
||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
|
||||
case OutputRedirect::FileDescriptors:
|
||||
return true;
|
||||
#endif
|
||||
#if defined(CATCH_CONFIG_USE_ASYNC)
|
||||
case OutputRedirect::Pipes:
|
||||
return true;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
@@ -267,7 +454,8 @@ namespace Catch {
|
||||
#if defined( CATCH_CONFIG_NEW_CAPTURE )
|
||||
return Detail::make_unique<FileRedirect>();
|
||||
#else
|
||||
return Detail::make_unique<StreamRedirect>();
|
||||
//return Detail::make_unique<StreamRedirect>();
|
||||
return Detail::make_unique<PipeRedirect>();
|
||||
#endif
|
||||
} else {
|
||||
return Detail::make_unique<NoopRedirect>();
|
||||
|
@@ -25,8 +25,10 @@ namespace Catch {
|
||||
None,
|
||||
//! Redirect std::cout/std::cerr/std::clog streams internally
|
||||
Streams,
|
||||
//! Redirect the stdout/stderr file descriptors into files
|
||||
//! Redirect the stdout/stderr file descriptors into temp files
|
||||
FileDescriptors,
|
||||
//! Redirect the stdout/stderr file descriptors into anonymous pipes
|
||||
Pipes,
|
||||
};
|
||||
|
||||
virtual ~OutputRedirect(); // = default;
|
||||
|
@@ -308,10 +308,15 @@ TEST_CASE( "X/level/0/b", "[Tricky][fizz]" ){ SUCCEED(""); }
|
||||
TEST_CASE( "X/level/1/a", "[Tricky]" ) { SUCCEED(""); }
|
||||
TEST_CASE( "X/level/1/b", "[Tricky]" ) { SUCCEED(""); }
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
TEST_CASE( "has printf" ) {
|
||||
|
||||
// This can cause problems as, currently, stdout itself is not redirected - only the cout (and cerr) buffer
|
||||
printf( "loose text artifact\n" );
|
||||
fflush( stdout );
|
||||
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
Reference in New Issue
Block a user