2017-07-15 16:48:21 +02:00
# include "catch_run_context.hpp"
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 09:08:17 +02: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 " ) ;
}
}