2017-07-15 16:48:21 +02:00
#include "catch_run_context.hpp"
2017-07-19 10:13:47 +02:00
#include <cassert>
2017-07-25 21:57:35 +02:00
#include <algorithm>
2017-07-19 10:13:47 +02:00
2017-07-15 16:48:21 +02:00
namespace Catch {
StreamRedirect :: StreamRedirect ( std :: ostream & stream , std :: string & targetString )
: m_stream ( stream ),
m_prevBuf ( stream . rdbuf ()),
m_targetString ( targetString ) {
stream . rdbuf ( m_oss . rdbuf ());
}
StreamRedirect ::~ StreamRedirect () {
m_targetString += m_oss . str ();
m_stream . rdbuf ( m_prevBuf );
}
RunContext :: RunContext ( IConfigPtr const & _config , IStreamingReporterPtr && reporter )
: m_runInfo ( _config -> name ()),
m_context ( getCurrentMutableContext ()),
m_config ( _config ),
m_reporter ( std :: move ( reporter )) {
m_context . setRunner ( this );
m_context . setConfig ( m_config );
m_context . setResultCapture ( this );
m_reporter -> testRunStarting ( m_runInfo );
}
RunContext ::~ RunContext () {
m_reporter -> testRunEnded ( TestRunStats ( m_runInfo , m_totals , aborting ()));
}
void RunContext :: testGroupStarting ( std :: string const & testSpec , std :: size_t groupIndex , std :: size_t groupsCount ) {
m_reporter -> testGroupStarting ( GroupInfo ( testSpec , groupIndex , groupsCount ));
}
void RunContext :: testGroupEnded ( std :: string const & testSpec , Totals const & totals , std :: size_t groupIndex , std :: size_t groupsCount ) {
m_reporter -> testGroupEnded ( TestGroupStats ( GroupInfo ( testSpec , groupIndex , groupsCount ), totals , aborting ()));
}
Totals RunContext :: runTest ( TestCase const & testCase ) {
Totals prevTotals = m_totals ;
std :: string redirectedCout ;
std :: string redirectedCerr ;
TestCaseInfo testInfo = testCase . getTestCaseInfo ();
m_reporter -> testCaseStarting ( testInfo );
m_activeTestCase = & testCase ;
ITracker & rootTracker = m_trackerContext . startRun ();
assert ( rootTracker . isSectionTracker ());
static_cast < SectionTracker &> ( rootTracker ). addInitialFilters ( m_config -> getSectionsToRun ());
do {
m_trackerContext . startCycle ();
m_testCaseTracker = & SectionTracker :: acquire ( m_trackerContext , TestCaseTracking :: NameAndLocation ( testInfo . name , testInfo . lineInfo ));
runCurrentTest ( redirectedCout , redirectedCerr );
} while ( ! m_testCaseTracker -> isSuccessfullyCompleted () && ! aborting ());
Totals deltaTotals = m_totals . delta ( prevTotals );
if ( testInfo . expectedToFail () && deltaTotals . testCases . passed > 0 ) {
deltaTotals . assertions . failed ++ ;
deltaTotals . testCases . passed -- ;
deltaTotals . testCases . failed ++ ;
}
m_totals . testCases += deltaTotals . testCases ;
m_reporter -> testCaseEnded ( TestCaseStats ( testInfo ,
deltaTotals ,
redirectedCout ,
redirectedCerr ,
aborting ()));
m_activeTestCase = nullptr ;
m_testCaseTracker = nullptr ;
return deltaTotals ;
}
IConfigPtr RunContext :: config () const {
return m_config ;
}
IStreamingReporter & RunContext :: reporter () const {
return * m_reporter ;
}
void RunContext :: assertionEnded ( AssertionResult const & result ) {
if ( result . getResultType () == ResultWas :: Ok ) {
m_totals . assertions . passed ++ ;
} else if ( ! result . isOk ()) {
m_totals . assertions . failed ++ ;
}
// We have no use for the return value (whether messages should be cleared), because messages were made scoped
// and should be let to clear themselves out.
static_cast < void > ( m_reporter -> assertionEnded ( AssertionStats ( result , m_messages , m_totals )));
// Reset working state
m_lastAssertionInfo = AssertionInfo ( "" , m_lastAssertionInfo . lineInfo , "{Unknown expression after the reported line}" , m_lastAssertionInfo . resultDisposition );
m_lastResult = result ;
}
bool RunContext :: sectionStarted ( SectionInfo const & sectionInfo , Counts & assertions ) {
ITracker & sectionTracker = SectionTracker :: acquire ( m_trackerContext , TestCaseTracking :: NameAndLocation ( sectionInfo . name , sectionInfo . lineInfo ));
if ( ! sectionTracker . isOpen ())
return false ;
m_activeSections . push_back ( & sectionTracker );
m_lastAssertionInfo . lineInfo = sectionInfo . lineInfo ;
m_reporter -> sectionStarting ( sectionInfo );
assertions = m_totals . assertions ;
return true ;
}
bool RunContext :: testForMissingAssertions ( Counts & assertions ) {
if ( assertions . total () != 0 )
return false ;
if ( ! m_config -> warnAboutMissingAssertions ())
return false ;
if ( m_trackerContext . currentTracker (). hasChildren ())
return false ;
m_totals . assertions . failed ++ ;
assertions . failed ++ ;
return true ;
}
void RunContext :: sectionEnded ( SectionEndInfo const & endInfo ) {
Counts assertions = m_totals . assertions - endInfo . prevAssertions ;
bool missingAssertions = testForMissingAssertions ( assertions );
if ( ! m_activeSections . empty ()) {
m_activeSections . back () -> close ();
m_activeSections . pop_back ();
}
m_reporter -> sectionEnded ( SectionStats ( endInfo . sectionInfo , assertions , endInfo . durationInSeconds , missingAssertions ));
m_messages . clear ();
}
void RunContext :: sectionEndedEarly ( SectionEndInfo const & endInfo ) {
if ( m_unfinishedSections . empty ())
m_activeSections . back () -> fail ();
else
m_activeSections . back () -> close ();
m_activeSections . pop_back ();
m_unfinishedSections . push_back ( endInfo );
}
void RunContext :: pushScopedMessage ( MessageInfo const & message ) {
m_messages . push_back ( message );
}
void RunContext :: popScopedMessage ( MessageInfo const & message ) {
m_messages . erase ( std :: remove ( m_messages . begin (), m_messages . end (), message ), m_messages . end ());
}
std :: string RunContext :: getCurrentTestName () const {
return m_activeTestCase
? m_activeTestCase -> getTestCaseInfo (). name
: std :: string ();
}
const AssertionResult * RunContext :: getLastResult () const {
return & m_lastResult ;
}
void RunContext :: exceptionEarlyReported () {
m_shouldReportUnexpected = false ;
}
void RunContext :: handleFatalErrorCondition ( std :: string const & message ) {
// Don't rebuild the result -- the stringification itself can cause more fatal errors
// Instead, fake a result data.
AssertionResultData tempResult ;
tempResult . resultType = ResultWas :: FatalErrorCondition ;
tempResult . message = message ;
AssertionResult result ( m_lastAssertionInfo , tempResult );
getResultCapture (). assertionEnded ( result );
handleUnfinishedSections ();
// Recreate section for test case (as we will lose the one that was in scope)
TestCaseInfo const & testCaseInfo = m_activeTestCase -> getTestCaseInfo ();
SectionInfo testCaseSection ( testCaseInfo . lineInfo , testCaseInfo . name , testCaseInfo . description );
Counts assertions ;
assertions . failed = 1 ;
SectionStats testCaseSectionStats ( testCaseSection , assertions , 0 , false );
m_reporter -> sectionEnded ( testCaseSectionStats );
TestCaseInfo testInfo = m_activeTestCase -> getTestCaseInfo ();
Totals deltaTotals ;
deltaTotals . testCases . failed = 1 ;
m_reporter -> testCaseEnded ( TestCaseStats ( testInfo ,
deltaTotals ,
std :: string (),
std :: string (),
false ));
m_totals . testCases . failed ++ ;
testGroupEnded ( std :: string (), m_totals , 1 , 1 );
m_reporter -> testRunEnded ( TestRunStats ( m_runInfo , m_totals , false ));
}
bool RunContext :: aborting () const {
return m_totals . assertions . failed == static_cast < std :: size_t > ( m_config -> abortAfter ());
}
void RunContext :: runCurrentTest ( std :: string & redirectedCout , std :: string & redirectedCerr ) {
TestCaseInfo const & testCaseInfo = m_activeTestCase -> getTestCaseInfo ();
SectionInfo testCaseSection ( testCaseInfo . lineInfo , testCaseInfo . name , testCaseInfo . description );
m_reporter -> sectionStarting ( testCaseSection );
Counts prevAssertions = m_totals . assertions ;
double duration = 0 ;
m_shouldReportUnexpected = true ;
try {
m_lastAssertionInfo = AssertionInfo ( "TEST_CASE" , testCaseInfo . lineInfo , "" , ResultDisposition :: Normal );
seedRng ( * m_config );
Timer timer ;
timer . start ();
if ( m_reporter -> getPreferences (). shouldRedirectStdOut ) {
StreamRedirect coutRedir ( cout (), redirectedCout );
StreamRedirect cerrRedir ( cerr (), redirectedCerr );
invokeActiveTestCase ();
} else {
invokeActiveTestCase ();
}
duration = timer . getElapsedSeconds ();
} catch ( TestFailureException & ) {
// This just means the test was aborted due to failure
} catch (...) {
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin.
if ( m_shouldReportUnexpected ) {
makeUnexpectedResultBuilder (). useActiveException ();
}
}
m_testCaseTracker -> close ();
handleUnfinishedSections ();
m_messages . clear ();
Counts assertions = m_totals . assertions - prevAssertions ;
bool missingAssertions = testForMissingAssertions ( assertions );
if ( testCaseInfo . okToFail ()) {
std :: swap ( assertions . failedButOk , assertions . failed );
m_totals . assertions . failed -= assertions . failedButOk ;
m_totals . assertions . failedButOk += assertions . failedButOk ;
}
SectionStats testCaseSectionStats ( testCaseSection , assertions , duration , missingAssertions );
m_reporter -> sectionEnded ( testCaseSectionStats );
}
void RunContext :: invokeActiveTestCase () {
FatalConditionHandler fatalConditionHandler ; // Handle signals
m_activeTestCase -> invoke ();
fatalConditionHandler . reset ();
}
ResultBuilder RunContext :: makeUnexpectedResultBuilder () const {
return ResultBuilder ( m_lastAssertionInfo . macroName ,
m_lastAssertionInfo . lineInfo ,
m_lastAssertionInfo . capturedExpression ,
m_lastAssertionInfo . resultDisposition );
}
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.
2017-07-18 08:08:17 +01:00
for ( auto it = m_unfinishedSections . rbegin (),
2017-07-15 16:48:21 +02:00
itEnd = m_unfinishedSections . rend ();
it != itEnd ;
++ it )
sectionEnded ( * it );
m_unfinishedSections . clear ();
}
IResultCapture & getResultCapture () {
if ( IResultCapture * capture = getCurrentContext (). getResultCapture ())
return * capture ;
else
CATCH_INTERNAL_ERROR ( "No result capture instance" );
}
}