Capture std::clog writes and combine them with std::cerr writes (#989)

This also introduces Catch::clog() method to allow embedded targets
to override std::clog usage with their own stream (presumably null-sink),
similarly to how Catch::cout() and Catch::cerr() are used.

Fixes #989
This commit is contained in:
Martin Hořeňovský 2017-08-09 15:28:40 +02:00
parent 92d714ee12
commit 7e4038d848
8 changed files with 116 additions and 7 deletions

View File

@ -22,6 +22,7 @@
#include "catch_result_builder.h" #include "catch_result_builder.h"
#include "catch_fatal_condition.hpp" #include "catch_fatal_condition.hpp"
#include <set> #include <set>
#include <string> #include <string>
@ -50,6 +51,29 @@ namespace Catch {
std::string& m_targetString; std::string& m_targetString;
}; };
// 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 and cannot use StreamRedirect on its own
class StdErrRedirect {
public:
StdErrRedirect(std::string& targetString)
:m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()),
m_targetString(targetString){
cerr().rdbuf(m_oss.rdbuf());
clog().rdbuf(m_oss.rdbuf());
}
~StdErrRedirect() {
m_targetString += m_oss.str();
cerr().rdbuf(m_cerrBuf);
clog().rdbuf(m_clogBuf);
}
private:
std::streambuf* m_cerrBuf;
std::streambuf* m_clogBuf;
std::ostringstream m_oss;
std::string& m_targetString;
};
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
class RunContext : public IResultCapture, public IRunner { class RunContext : public IResultCapture, public IRunner {
@ -305,7 +329,7 @@ namespace Catch {
timer.start(); timer.start();
if( m_reporter->getPreferences().shouldRedirectStdOut ) { if( m_reporter->getPreferences().shouldRedirectStdOut ) {
StreamRedirect coutRedir( Catch::cout(), redirectedCout ); StreamRedirect coutRedir( Catch::cout(), redirectedCout );
StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); StdErrRedirect errRedir( redirectedCerr );
invokeActiveTestCase(); invokeActiveTestCase();
} }
else { else {

View File

@ -21,6 +21,7 @@ namespace Catch {
std::ostream& cout(); std::ostream& cout();
std::ostream& cerr(); std::ostream& cerr();
std::ostream& clog();
struct IStream { struct IStream {

View File

@ -103,6 +103,9 @@ namespace Catch {
std::ostream& cerr() { std::ostream& cerr() {
return std::cerr; return std::cerr;
} }
std::ostream& clog() {
return std::clog;
}
#endif #endif
} }

View File

@ -622,6 +622,9 @@ with messages:
A string sent directly to stdout A string sent directly to stdout
A string sent directly to stderr A string sent directly to stderr
Write to std::cerr
Write to std::clog
Interleaved writes to error streams
Message from section one Message from section one
Message from section two Message from section two
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -953,6 +956,6 @@ with expansion:
"first" == "second" "first" == "second"
=============================================================================== ===============================================================================
test cases: 168 | 119 passed | 45 failed | 4 failed as expected test cases: 169 | 120 passed | 45 failed | 4 failed as expected
assertions: 968 | 859 passed | 88 failed | 21 failed as expected assertions: 968 | 859 passed | 88 failed | 21 failed as expected

View File

@ -6786,6 +6786,39 @@ PASSED:
with expansion: with expansion:
Approx( 1.23 ) != 1.24 Approx( 1.23 ) != 1.24
Write to std::cerr
-------------------------------------------------------------------------------
Standard error is reported and redirected
std::cerr
-------------------------------------------------------------------------------
MessageTests.cpp:<line number>
...............................................................................
No assertions in section 'std::cerr'
Write to std::clog
-------------------------------------------------------------------------------
Standard error is reported and redirected
std::clog
-------------------------------------------------------------------------------
MessageTests.cpp:<line number>
...............................................................................
No assertions in section 'std::clog'
Interleaved writes to error streams
-------------------------------------------------------------------------------
Standard error is reported and redirected
Interleaved writes to cerr and clog
-------------------------------------------------------------------------------
MessageTests.cpp:<line number>
...............................................................................
No assertions in section 'Interleaved writes to cerr and clog'
Message from section one Message from section one
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Standard output from all sections is reported Standard output from all sections is reported
@ -9483,6 +9516,6 @@ MiscTests.cpp:<line number>:
PASSED: PASSED:
=============================================================================== ===============================================================================
test cases: 168 | 118 passed | 46 failed | 4 failed as expected test cases: 169 | 118 passed | 47 failed | 4 failed as expected
assertions: 970 | 859 passed | 90 failed | 21 failed as expected assertions: 973 | 859 passed | 93 failed | 21 failed as expected

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesspanner> <testsuitesspanner>
<testsuite name="<exe-name>" errors="13" failures="78" tests="971" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <testsuite name="<exe-name>" errors="13" failures="81" tests="974" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testcase classname="global" name="# A test name that starts with a #" time="{duration}"/> <testcase classname="global" name="# A test name that starts with a #" time="{duration}"/>
<testcase classname="#748 - captures with unexpected exceptions" name="outside assertions" time="{duration}"> <testcase classname="#748 - captures with unexpected exceptions" name="outside assertions" time="{duration}">
<error type="TEST_CASE"> <error type="TEST_CASE">
@ -475,6 +475,13 @@ A string sent directly to stderr
</system-err> </system-err>
</testcase> </testcase>
<testcase classname="global" name="Some simple comparisons between doubles" time="{duration}"/> <testcase classname="global" name="Some simple comparisons between doubles" time="{duration}"/>
<testcase classname="Standard error is reported and redirected" name="Interleaved writes to cerr and clog" time="{duration}">
<system-err>
Write to std::cerr
Write to std::clog
Interleaved writes to error streams
</system-err>
</testcase>
<testcase classname="Standard output from all sections is reported" name="two" time="{duration}"> <testcase classname="Standard output from all sections is reported" name="two" time="{duration}">
<system-out> <system-out>
Message from section one Message from section one
@ -750,6 +757,9 @@ hello
</system-out> </system-out>
<system-err> <system-err>
A string sent directly to stderr A string sent directly to stderr
Write to std::cerr
Write to std::clog
Interleaved writes to error streams
</system-err> </system-err>
</testsuite> </testsuite>
</testsuites> </testsuites>

View File

@ -7309,6 +7309,24 @@ A string sent directly to stderr
</Expression> </Expression>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Standard error is reported and redirected" tags="[.][hide][messages]" filename="projects/<exe-name>/MessageTests.cpp" >
<Section name="std::cerr" filename="projects/<exe-name>/MessageTests.cpp" >
<OverallResults successes="0" failures="1" expectedFailures="0"/>
</Section>
<Section name="std::clog" filename="projects/<exe-name>/MessageTests.cpp" >
<OverallResults successes="0" failures="1" expectedFailures="0"/>
</Section>
<Section name="Interleaved writes to cerr and clog" filename="projects/<exe-name>/MessageTests.cpp" >
<OverallResults successes="0" failures="1" expectedFailures="0"/>
</Section>
<OverallResult success="false">
<StdErr>
Write to std::cerr
Write to std::clog
Interleaved writes to error streams
</StdErr>
</OverallResult>
</TestCase>
<TestCase name="Standard output from all sections is reported" tags="[.][hide][messages]" filename="projects/<exe-name>/MessageTests.cpp" > <TestCase name="Standard output from all sections is reported" tags="[.][hide][messages]" filename="projects/<exe-name>/MessageTests.cpp" >
<Section name="one" filename="projects/<exe-name>/MessageTests.cpp" > <Section name="one" filename="projects/<exe-name>/MessageTests.cpp" >
<OverallResults successes="0" failures="1" expectedFailures="0"/> <OverallResults successes="0" failures="1" expectedFailures="0"/>
@ -10143,7 +10161,7 @@ spanner <OverallResult success="true"/>
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="859" failures="91" expectedFailures="21"/> <OverallResults successes="859" failures="94" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="859" failures="90" expectedFailures="21"/> <OverallResults successes="859" failures="93" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -99,6 +99,23 @@ TEST_CASE( "Standard output from all sections is reported", "[messages][.]" )
} }
} }
TEST_CASE( "Standard error is reported and redirected", "[messages][.]" ) {
SECTION( "std::cerr" ) {
std::cerr << "Write to std::cerr" << std::endl;
}
SECTION( "std::clog" ) {
std::clog << "Write to std::clog" << std::endl;
}
SECTION( "Interleaved writes to cerr and clog" ) {
std::cerr << "Inter";
std::clog << "leaved";
std::cerr << ' ';
std::clog << "writes";
std::cerr << " to error";
std::clog << " streams\n";
}
}
TEST_CASE( "SCOPED_INFO is reset for each loop", "[messages][failing][.]" ) TEST_CASE( "SCOPED_INFO is reset for each loop", "[messages][failing][.]" )
{ {
for( int i=0; i<100; i++ ) for( int i=0; i<100; i++ )