2011-12-28 11:23:32 +01:00
/*
2010-11-10 00:24:00 +01:00
* Created by Phil on 22 / 10 / 2010.
* Copyright 2010 Two Blue Cubes Ltd . All rights reserved .
*
* Distributed under the Boost Software License , Version 1.0 . ( See accompanying
* file LICENSE_1_0 . txt or copy at http : //www.boost.org/LICENSE_1_0.txt)
*/
2012-09-17 07:42:29 +02:00
# ifndef TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
# define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
2010-11-10 00:24:00 +01:00
2011-01-11 10:13:31 +01:00
# include "catch_interfaces_runner.h"
2010-12-31 23:46:51 +01:00
# include "catch_interfaces_reporter.h"
2012-05-11 09:16:39 +02:00
# include "catch_interfaces_exception.h"
2011-01-01 01:29:58 +01:00
# include "catch_config.hpp"
2011-01-05 22:16:54 +01:00
# include "catch_test_registry.hpp"
2012-08-14 20:30:30 +02:00
# include "catch_test_case_info.h"
2010-11-10 00:24:00 +01:00
# include "catch_capture.hpp"
2012-02-23 09:49:52 +01:00
# include "catch_totals.hpp"
2014-05-16 19:28:58 +02:00
# include "catch_test_spec.hpp"
2013-07-24 20:13:08 +02:00
# include "catch_test_case_tracker.hpp"
2013-08-07 19:56:35 +02:00
# include "catch_timer.h"
2014-05-28 19:53:01 +02:00
# include "catch_result_builder.h"
2014-08-22 09:07:39 +02:00
# include "catch_fatal_condition.hpp"
2010-11-10 00:24:00 +01:00
2011-02-08 09:42:05 +01:00
# include <set>
# include <string>
2012-05-16 09:02:20 +02:00
namespace Catch {
class StreamRedirect {
2013-07-03 20:14:59 +02:00
2011-02-21 09:50:05 +01:00
public :
2012-05-16 09:02:20 +02:00
StreamRedirect ( std : : ostream & stream , std : : string & targetString )
2011-02-21 09:50:05 +01:00
: m_stream ( stream ) ,
m_prevBuf ( stream . rdbuf ( ) ) ,
2013-07-03 20:14:59 +02:00
m_targetString ( targetString )
{
2011-02-21 09:50:05 +01:00
stream . rdbuf ( m_oss . rdbuf ( ) ) ;
}
2013-07-03 20:14:59 +02:00
2012-05-16 09:02:20 +02:00
~ StreamRedirect ( ) {
2011-12-28 11:23:32 +01:00
m_targetString + = m_oss . str ( ) ;
2011-02-21 09:50:05 +01:00
m_stream . rdbuf ( m_prevBuf ) ;
}
2013-07-03 20:14:59 +02:00
2011-02-21 09:50:05 +01:00
private :
std : : ostream & m_stream ;
std : : streambuf * m_prevBuf ;
std : : ostringstream m_oss ;
std : : string & m_targetString ;
} ;
2012-11-04 22:09:22 +01:00
2011-01-28 19:56:26 +01:00
///////////////////////////////////////////////////////////////////////////
2012-05-16 09:02:20 +02:00
2013-06-05 09:18:52 +02:00
class RunContext : public IResultCapture , public IRunner {
2013-07-03 20:14:59 +02:00
2013-06-05 09:18:52 +02:00
RunContext ( RunContext const & ) ;
void operator = ( RunContext const & ) ;
2013-07-03 20:14:59 +02:00
2010-11-10 00:24:00 +01:00
public :
2011-01-11 20:48:48 +01:00
2017-04-29 20:38:34 +02:00
explicit RunContext ( IConfigPtr const & _config , IStreamingReporterPtr & & reporter )
2015-07-01 08:50:53 +02:00
: m_runInfo ( _config - > name ( ) ) ,
2012-12-02 00:54:17 +01:00
m_context ( getCurrentMutableContext ( ) ) ,
2015-07-01 08:50:53 +02:00
m_config ( _config ) ,
2017-04-29 20:38:34 +02:00
m_reporter ( std : : move ( reporter ) )
2010-11-10 00:24:00 +01:00
{
2012-05-21 19:52:09 +02:00
m_context . setRunner ( this ) ;
2013-05-28 19:51:53 +02:00
m_context . setConfig ( m_config ) ;
2012-05-21 19:52:09 +02:00
m_context . setResultCapture ( this ) ;
2012-12-02 00:54:17 +01:00
m_reporter - > testRunStarting ( m_runInfo ) ;
2010-11-10 00:24:00 +01:00
}
2013-07-03 20:14:59 +02:00
2013-06-05 09:18:52 +02:00
virtual ~ RunContext ( ) {
2013-01-03 10:04:46 +01:00
m_reporter - > testRunEnded ( TestRunStats ( m_runInfo , m_totals , aborting ( ) ) ) ;
2010-11-10 00:24:00 +01:00
}
2012-07-16 09:58:28 +02:00
2013-01-13 22:51:44 +01:00
void testGroupStarting ( std : : string const & testSpec , std : : size_t groupIndex , std : : size_t groupsCount ) {
m_reporter - > testGroupStarting ( GroupInfo ( testSpec , groupIndex , groupsCount ) ) ;
2012-11-30 10:13:27 +01:00
}
2013-01-13 22:51:44 +01:00
void 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 ( ) ) ) ;
2012-11-30 10:13:27 +01:00
}
2013-04-23 19:58:56 +02:00
Totals runTest ( TestCase const & testCase ) {
2012-02-23 09:49:52 +01:00
Totals prevTotals = m_totals ;
2011-01-19 20:30:01 +01:00
std : : string redirectedCout ;
std : : string redirectedCerr ;
2012-11-25 12:19:55 +01:00
TestCaseInfo testInfo = testCase . getTestCaseInfo ( ) ;
2012-11-25 22:43:36 +01:00
2012-11-30 20:15:23 +01:00
m_reporter - > testCaseStarting ( testInfo ) ;
2013-07-03 20:14:59 +02:00
2013-07-25 08:49:00 +02:00
m_activeTestCase = & testCase ;
2015-11-04 19:01:28 +01:00
2010-11-30 07:48:21 +01:00
2017-06-05 16:49:10 +02:00
ITracker & rootTracker = m_trackerContext . startRun ( ) ;
assert ( rootTracker . isSectionTracker ( ) ) ;
static_cast < SectionTracker & > ( rootTracker ) . addInitialFilters ( m_config - > getSectionsToRun ( ) ) ;
2012-05-16 09:02:20 +02:00
do {
2017-06-05 16:49:10 +02:00
m_trackerContext . startCycle ( ) ;
m_testCaseTracker = & SectionTracker : : acquire ( m_trackerContext , TestCaseTracking : : NameAndLocation ( testInfo . name , testInfo . lineInfo ) ) ;
runCurrentTest ( redirectedCout , redirectedCerr ) ;
2010-11-10 00:24:00 +01:00
}
2017-06-05 16:49:10 +02:00
while ( ! m_testCaseTracker - > isSuccessfullyCompleted ( ) & & ! aborting ( ) ) ;
2011-01-19 09:52:25 +01:00
2012-11-21 09:19:23 +01:00
Totals deltaTotals = m_totals . delta ( prevTotals ) ;
2016-03-14 20:13:34 +01:00
if ( testInfo . expectedToFail ( ) & & deltaTotals . testCases . passed > 0 ) {
deltaTotals . assertions . failed + + ;
deltaTotals . testCases . passed - - ;
deltaTotals . testCases . failed + + ;
}
2012-11-21 09:19:23 +01:00
m_totals . testCases + = deltaTotals . testCases ;
2013-01-03 10:04:46 +01:00
m_reporter - > testCaseEnded ( TestCaseStats ( testInfo ,
deltaTotals ,
redirectedCout ,
redirectedCerr ,
aborting ( ) ) ) ;
2012-11-21 09:19:23 +01:00
2017-04-25 12:41:30 +02:00
m_activeTestCase = nullptr ;
m_testCaseTracker = nullptr ;
2011-01-19 20:30:01 +01:00
2012-08-15 20:12:51 +02:00
return deltaTotals ;
2010-11-10 00:24:00 +01:00
}
2012-02-17 20:50:59 +01:00
2017-04-25 21:18:02 +02:00
IConfigPtr config ( ) const {
2012-06-05 21:50:47 +02:00
return m_config ;
}
2017-04-29 20:38:34 +02:00
IStreamingReporter & reporter ( ) const {
return * m_reporter ;
}
2013-07-03 20:14:59 +02:00
2011-01-11 10:13:31 +01:00
private : // IResultCapture
2010-12-31 21:49:58 +01:00
2013-04-23 19:58:56 +02:00
virtual void assertionEnded ( AssertionResult const & result ) {
2012-05-16 09:02:20 +02:00
if ( result . getResultType ( ) = = ResultWas : : Ok ) {
2012-02-23 09:49:52 +01:00
m_totals . assertions . passed + + ;
2010-12-27 23:18:33 +01:00
}
2012-11-13 10:44:52 +01:00
else if ( ! result . isOk ( ) ) {
2012-02-23 09:49:52 +01:00
m_totals . assertions . failed + + ;
2010-12-27 23:05:13 +01:00
}
2013-07-03 20:14:59 +02:00
2013-06-28 17:25:49 +02:00
if ( m_reporter - > assertionEnded ( AssertionStats ( result , m_messages , m_totals ) ) )
m_messages . clear ( ) ;
2012-11-17 18:22:37 +01:00
2013-02-02 21:36:36 +01:00
// Reset working state
2017-01-15 09:41:33 +01:00
m_lastAssertionInfo = AssertionInfo ( std : : string ( ) , m_lastAssertionInfo . lineInfo , " {Unknown expression after the reported line} " , m_lastAssertionInfo . resultDisposition ) ;
2014-05-28 19:53:01 +02:00
m_lastResult = result ;
2010-11-10 00:24:00 +01:00
}
2013-07-03 20:14:59 +02:00
2012-05-16 09:02:20 +02:00
virtual bool sectionStarted (
2012-11-30 09:58:46 +01:00
SectionInfo const & sectionInfo ,
2012-02-23 09:57:51 +01:00
Counts & assertions
2011-01-11 20:48:48 +01:00
)
2010-11-29 20:40:44 +01:00
{
2017-01-12 18:10:38 +01:00
ITracker & sectionTracker = SectionTracker : : acquire ( m_trackerContext , TestCaseTracking : : NameAndLocation ( sectionInfo . name , sectionInfo . lineInfo ) ) ;
2015-11-02 20:21:46 +01:00
if ( ! sectionTracker . isOpen ( ) )
2011-01-19 09:52:25 +01:00
return false ;
2015-11-02 20:21:46 +01:00
m_activeSections . push_back ( & sectionTracker ) ;
2015-11-04 19:01:28 +01:00
2012-11-30 09:58:46 +01:00
m_lastAssertionInfo . lineInfo = sectionInfo . lineInfo ;
2012-11-04 22:39:16 +01:00
2012-11-30 20:15:23 +01:00
m_reporter - > sectionStarting ( sectionInfo ) ;
2012-11-29 10:05:51 +01:00
2012-02-23 09:57:51 +01:00
assertions = m_totals . assertions ;
2013-07-03 20:14:59 +02:00
2010-11-29 20:40:44 +01:00
return true ;
}
2013-07-26 20:26:08 +02:00
bool testForMissingAssertions ( Counts & assertions ) {
2015-11-02 20:21:46 +01:00
if ( assertions . total ( ) ! = 0 )
return false ;
2015-11-03 18:05:08 +01:00
if ( ! m_config - > warnAboutMissingAssertions ( ) )
2015-11-02 20:21:46 +01:00
return false ;
if ( m_trackerContext . currentTracker ( ) . hasChildren ( ) )
2013-07-26 20:26:08 +02:00
return false ;
m_totals . assertions . failed + + ;
assertions . failed + + ;
return true ;
}
2015-11-04 19:01:28 +01:00
2015-09-27 03:12:21 +02:00
virtual void sectionEnded ( SectionEndInfo const & endInfo ) {
Counts assertions = m_totals . assertions - endInfo . prevAssertions ;
2013-07-26 20:26:08 +02:00
bool missingAssertions = testForMissingAssertions ( assertions ) ;
2015-11-04 19:01:28 +01:00
2015-11-02 20:21:46 +01:00
if ( ! m_activeSections . empty ( ) ) {
m_activeSections . back ( ) - > close ( ) ;
m_activeSections . pop_back ( ) ;
}
2015-11-04 19:01:28 +01:00
2015-09-27 03:12:21 +02:00
m_reporter - > sectionEnded ( SectionStats ( endInfo . sectionInfo , assertions , endInfo . durationInSeconds , missingAssertions ) ) ;
2013-02-02 20:58:04 +01:00
m_messages . clear ( ) ;
2010-11-29 20:40:44 +01:00
}
2010-12-27 12:09:34 +01:00
2015-09-27 03:12:21 +02:00
virtual void sectionEndedEarly ( SectionEndInfo const & endInfo ) {
2015-11-02 20:21:46 +01:00
if ( m_unfinishedSections . empty ( ) )
m_activeSections . back ( ) - > fail ( ) ;
else
m_activeSections . back ( ) - > close ( ) ;
m_activeSections . pop_back ( ) ;
2015-11-04 19:01:28 +01:00
2015-09-27 03:12:21 +02:00
m_unfinishedSections . push_back ( endInfo ) ;
}
2015-11-04 19:01:28 +01:00
2013-06-28 18:09:57 +02:00
virtual void pushScopedMessage ( MessageInfo const & message ) {
m_messages . push_back ( message ) ;
2013-02-02 20:58:04 +01:00
}
2013-07-03 20:14:59 +02:00
2013-06-28 18:09:57 +02:00
virtual void popScopedMessage ( MessageInfo const & message ) {
m_messages . erase ( std : : remove ( m_messages . begin ( ) , m_messages . end ( ) , message ) , m_messages . end ( ) ) ;
2013-02-02 20:58:04 +01:00
}
2011-01-11 20:48:48 +01:00
2012-05-16 09:02:20 +02:00
virtual std : : string getCurrentTestName ( ) const {
2013-07-25 08:49:00 +02:00
return m_activeTestCase
? m_activeTestCase - > getTestCaseInfo ( ) . name
2017-01-15 09:41:33 +01:00
: std : : string ( ) ;
2011-01-27 00:23:33 +01:00
}
2012-02-10 09:30:13 +01:00
2012-10-16 09:27:21 +02:00
virtual const AssertionResult * getLastResult ( ) const {
2013-07-03 20:14:59 +02:00
return & m_lastResult ;
2012-02-10 09:30:13 +01:00
}
2012-06-01 20:40:27 +02:00
2017-04-03 11:36:55 +02:00
virtual void exceptionEarlyReported ( ) {
m_shouldReportUnexpected = false ;
}
2014-08-22 20:33:28 +02:00
virtual void handleFatalErrorCondition ( std : : string const & message ) {
2017-03-02 18:18:28 +01:00
// 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 ) ;
2014-08-22 20:33:28 +02:00
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 ,
2017-01-15 09:41:33 +01:00
std : : string ( ) ,
std : : string ( ) ,
2014-08-22 20:33:28 +02:00
false ) ) ;
m_totals . testCases . failed + + ;
2017-01-15 09:41:33 +01:00
testGroupEnded ( std : : string ( ) , m_totals , 1 , 1 ) ;
2014-08-22 20:33:28 +02:00
m_reporter - > testRunEnded ( TestRunStats ( m_runInfo , m_totals , false ) ) ;
}
2012-08-23 21:08:50 +02:00
public :
// !TBD We need to do this another way!
2012-06-01 20:40:27 +02:00
bool aborting ( ) const {
2013-05-28 19:51:53 +02:00
return m_totals . assertions . failed = = static_cast < std : : size_t > ( m_config - > abortAfter ( ) ) ;
2012-06-01 20:40:27 +02:00
}
2012-08-23 21:08:50 +02:00
private :
2012-05-16 09:02:20 +02:00
void runCurrentTest ( std : : string & redirectedCout , std : : string & redirectedCerr ) {
2013-07-25 08:49:00 +02:00
TestCaseInfo const & testCaseInfo = m_activeTestCase - > getTestCaseInfo ( ) ;
2014-07-09 08:39:57 +02:00
SectionInfo testCaseSection ( testCaseInfo . lineInfo , testCaseInfo . name , testCaseInfo . description ) ;
2013-07-24 20:13:08 +02:00
m_reporter - > sectionStarting ( testCaseSection ) ;
2013-07-26 20:19:44 +02:00
Counts prevAssertions = m_totals . assertions ;
2013-08-07 19:56:35 +02:00
double duration = 0 ;
2017-04-03 11:48:50 +02:00
m_shouldReportUnexpected = true ;
2012-05-16 09:02:20 +02:00
try {
2017-01-15 09:41:33 +01:00
m_lastAssertionInfo = AssertionInfo ( " TEST_CASE " , testCaseInfo . lineInfo , std : : string ( ) , ResultDisposition : : Normal ) ;
2013-07-03 20:14:59 +02:00
2015-07-03 00:02:35 +02:00
seedRng ( * m_config ) ;
2015-11-04 19:01:28 +01:00
2013-08-07 19:56:35 +02:00
Timer timer ;
timer . start ( ) ;
2012-11-30 20:15:23 +01:00
if ( m_reporter - > getPreferences ( ) . shouldRedirectStdOut ) {
2014-10-02 20:08:19 +02:00
StreamRedirect coutRedir ( Catch : : cout ( ) , redirectedCout ) ;
StreamRedirect cerrRedir ( Catch : : cerr ( ) , redirectedCerr ) ;
2014-08-22 09:07:39 +02:00
invokeActiveTestCase ( ) ;
2012-02-17 10:28:21 +01:00
}
2012-05-16 09:02:20 +02:00
else {
2014-08-22 09:07:39 +02:00
invokeActiveTestCase ( ) ;
2012-02-17 10:28:21 +01:00
}
2013-08-07 19:56:35 +02:00
duration = timer . getElapsedSeconds ( ) ;
2011-01-19 09:52:25 +01:00
}
2012-05-16 09:02:20 +02:00
catch ( TestFailureException & ) {
2011-01-19 09:52:25 +01:00
// This just means the test was aborted due to failure
}
2012-05-16 09:02:20 +02:00
catch ( . . . ) {
2017-04-03 11:36:55 +02:00
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin.
if ( m_shouldReportUnexpected ) {
makeUnexpectedResultBuilder ( ) . useActiveException ( ) ;
}
2011-01-19 09:52:25 +01:00
}
2015-11-02 20:21:46 +01:00
m_testCaseTracker - > close ( ) ;
2014-08-22 20:33:28 +02:00
handleUnfinishedSections ( ) ;
2013-02-04 01:05:16 +01:00
m_messages . clear ( ) ;
2013-07-26 20:19:44 +02:00
Counts assertions = m_totals . assertions - prevAssertions ;
2013-07-26 20:26:08 +02:00
bool missingAssertions = testForMissingAssertions ( assertions ) ;
2013-07-26 20:19:44 +02:00
2014-07-03 09:09:57 +02:00
if ( testCaseInfo . okToFail ( ) ) {
std : : swap ( assertions . failedButOk , assertions . failed ) ;
m_totals . assertions . failed - = assertions . failedButOk ;
m_totals . assertions . failedButOk + = assertions . failedButOk ;
}
2013-08-07 19:56:35 +02:00
SectionStats testCaseSectionStats ( testCaseSection , assertions , duration , missingAssertions ) ;
2013-07-24 20:13:08 +02:00
m_reporter - > sectionEnded ( testCaseSectionStats ) ;
2011-01-19 09:52:25 +01:00
}
2012-05-10 22:46:46 +02:00
2014-08-22 09:07:39 +02:00
void invokeActiveTestCase ( ) {
FatalConditionHandler fatalConditionHandler ; // Handle signals
m_activeTestCase - > invoke ( ) ;
2014-10-02 19:28:45 +02:00
fatalConditionHandler . reset ( ) ;
2014-08-22 09:07:39 +02:00
}
2010-11-10 00:24:00 +01:00
private :
2014-08-22 20:33:28 +02:00
ResultBuilder makeUnexpectedResultBuilder ( ) const {
return ResultBuilder ( m_lastAssertionInfo . macroName . c_str ( ) ,
m_lastAssertionInfo . lineInfo ,
m_lastAssertionInfo . capturedExpression . c_str ( ) ,
m_lastAssertionInfo . resultDisposition ) ;
}
2015-11-04 19:01:28 +01:00
2014-08-22 20:33:28 +02:00
void handleUnfinishedSections ( ) {
// If sections ended prematurely due to an exception we stored their
// infos here so we can tear them down outside the unwind process.
2015-09-27 03:12:21 +02:00
for ( std : : vector < SectionEndInfo > : : const_reverse_iterator it = m_unfinishedSections . rbegin ( ) ,
2014-08-22 20:33:28 +02:00
itEnd = m_unfinishedSections . rend ( ) ;
it ! = itEnd ;
+ + it )
2015-09-27 03:12:21 +02:00
sectionEnded ( * it ) ;
2014-08-22 20:33:28 +02:00
m_unfinishedSections . clear ( ) ;
}
2012-12-02 00:54:17 +01:00
TestRunInfo m_runInfo ;
2012-05-21 19:52:09 +02:00
IMutableContext & m_context ;
2017-04-25 19:56:53 +02:00
TestCase const * m_activeTestCase = nullptr ;
2015-11-02 20:21:46 +01:00
ITracker * m_testCaseTracker ;
ITracker * m_currentSectionTracker ;
2012-10-16 09:27:21 +02:00
AssertionResult m_lastResult ;
2010-12-31 21:49:58 +01:00
2017-04-25 21:18:02 +02:00
IConfigPtr m_config ;
2012-02-23 09:49:52 +01:00
Totals m_totals ;
2017-04-25 21:42:01 +02:00
IStreamingReporterPtr m_reporter ;
2013-02-02 20:58:04 +01:00
std : : vector < MessageInfo > m_messages ;
2012-11-04 22:09:22 +01:00
AssertionInfo m_lastAssertionInfo ;
2015-09-27 03:12:21 +02:00
std : : vector < SectionEndInfo > m_unfinishedSections ;
2015-11-02 20:21:46 +01:00
std : : vector < ITracker * > m_activeSections ;
TrackerContext m_trackerContext ;
2017-04-25 19:56:53 +02:00
bool m_shouldReportUnexpected = true ;
2010-11-10 00:24:00 +01:00
} ;
2013-07-03 20:14:59 +02:00
2014-05-28 19:53:01 +02:00
IResultCapture & getResultCapture ( ) {
if ( IResultCapture * capture = getCurrentContext ( ) . getResultCapture ( ) )
return * capture ;
else
2017-05-05 16:42:57 +02:00
CATCH_INTERNAL_ERROR ( " No result capture instance " ) ;
2014-05-28 19:53:01 +02:00
}
2012-05-21 19:52:09 +02:00
} // end namespace Catch
2010-11-10 00:24:00 +01:00
2012-09-17 07:42:29 +02:00
# endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED