Added cutoff option to command line

Aborts testing after a certain number of assertion failures
This commit is contained in:
Phil Nash 2012-06-01 19:40:27 +01:00
parent 163088a11f
commit 19b2aa6187
14 changed files with 233 additions and 73 deletions

View File

@ -84,6 +84,7 @@ namespace Catch {
<< "\t-s, --success\n" << "\t-s, --success\n"
<< "\t-b, --break\n" << "\t-b, --break\n"
<< "\t-n, --name <name>\n\n" << "\t-n, --name <name>\n\n"
<< "\t-c, --cutoff [#]\n\n"
<< "For more detail usage please see: https://github.com/philsquared/Catch/wiki/Command-line" << std::endl; << "For more detail usage please see: https://github.com/philsquared/Catch/wiki/Command-line" << std::endl;
} }
inline void showHelp( std::string exeName ) { inline void showHelp( std::string exeName ) {

View File

@ -52,7 +52,8 @@ inline bool isTrue( bool value ){ return value; }
#define INTERNAL_CATCH_ACCEPT_EXPR( expr, stopOnFailure, originalExpr ) \ #define INTERNAL_CATCH_ACCEPT_EXPR( expr, stopOnFailure, originalExpr ) \
if( Catch::ResultAction::Value internal_catch_action = Catch::getCurrentContext().getResultCapture().acceptExpression( expr ) ) \ if( Catch::ResultAction::Value internal_catch_action = Catch::getCurrentContext().getResultCapture().acceptExpression( expr ) ) \
{ \ { \
if( internal_catch_action == Catch::ResultAction::DebugFailed ) BreakIntoDebugger(); \ if( internal_catch_action & Catch::ResultAction::Debug ) BreakIntoDebugger(); \
if( internal_catch_action & Catch::ResultAction::Abort ) throw Catch::TestFailureException(); \
if( Catch::isTrue( stopOnFailure ) ) throw Catch::TestFailureException(); \ if( Catch::isTrue( stopOnFailure ) ) throw Catch::TestFailureException(); \
if( Catch::isTrue( false ) ){ bool this_is_here_to_invoke_warnings = ( originalExpr ); Catch::isTrue( this_is_here_to_invoke_warnings ); } \ if( Catch::isTrue( false ) ){ bool this_is_here_to_invoke_warnings = ( originalExpr ); Catch::isTrue( this_is_here_to_invoke_warnings ); } \
} }

View File

@ -161,6 +161,19 @@ namespace Catch {
throw std::domain_error( cmd.name() + " does not accept arguments" ); throw std::domain_error( cmd.name() + " does not accept arguments" );
config.setShowHelp( true ); config.setShowHelp( true );
} }
if( Command cmd = parser.find( "-c", "--cutoff" ) ) {
if( cmd.argsCount() > 1 )
throw std::domain_error( cmd.name() + " only accepts 0-1 arguments" );
int threshold = 1;
if( cmd.argsCount() == 1 )
{
std::stringstream ss;
ss << cmd[0];
ss >> threshold;
}
config.setCutoff( threshold );
}
} }
catch( std::exception& ex ) { catch( std::exception& ex ) {
config.setError( ex.what() ); config.setError( ex.what() );

View File

@ -50,7 +50,8 @@ namespace Catch {
m_showHelp( false ), m_showHelp( false ),
m_streambuf( NULL ), m_streambuf( NULL ),
m_os( std::cout.rdbuf() ), m_os( std::cout.rdbuf() ),
m_includeWhichResults( Include::FailedOnly ) m_includeWhichResults( Include::FailedOnly ),
m_cutoff( -1 )
{} {}
~Config() { ~Config() {
@ -165,6 +166,14 @@ namespace Catch {
return m_includeWhichResults == Include::SuccessfulResults; return m_includeWhichResults == Include::SuccessfulResults;
} }
int getCutoff() const {
return m_cutoff;
}
void setCutoff( int cutoff ) {
m_cutoff = cutoff;
}
private: private:
Ptr<IReporter> m_reporter; Ptr<IReporter> m_reporter;
std::string m_filename; std::string m_filename;
@ -177,6 +186,7 @@ namespace Catch {
mutable std::ostream m_os; mutable std::ostream m_os;
Include::WhichResults m_includeWhichResults; Include::WhichResults m_includeWhichResults;
std::string m_name; std::string m_name;
int m_cutoff;
}; };
struct NewConfig { struct NewConfig {

View File

@ -38,6 +38,7 @@ namespace Catch
virtual void StartSection( const std::string& sectionName, const std::string& description ) = 0; virtual void StartSection( const std::string& sectionName, const std::string& description ) = 0;
virtual void EndSection( const std::string& sectionName, const Counts& assertions ) = 0; virtual void EndSection( const std::string& sectionName, const Counts& assertions ) = 0;
virtual void StartTestCase( const TestCaseInfo& testInfo ) = 0; virtual void StartTestCase( const TestCaseInfo& testInfo ) = 0;
virtual void Aborted() = 0;
virtual void EndTestCase( const TestCaseInfo& testInfo, const Totals& totals, const std::string& stdOut, const std::string& stdErr ) = 0; virtual void EndTestCase( const TestCaseInfo& testInfo, const Totals& totals, const std::string& stdOut, const std::string& stdErr ) = 0;
virtual void Result( const ResultInfo& result ) = 0; virtual void Result( const ResultInfo& result ) = 0;
}; };

View File

@ -31,8 +31,8 @@ struct ResultWas { enum OfType {
struct ResultAction { enum Value { struct ResultAction { enum Value {
None, None,
Failed = 1, // Failure - but no debug break if Debug bit not set Failed = 1, // Failure - but no debug break if Debug bit not set
DebugFailed = 3 // Indicates that the debugger should break, if possible Debug = 2, // If this bit is set, invoke the debugger
Abort = 4 // Test run should abort
}; }; }; };
} }

View File

@ -78,9 +78,15 @@ namespace Catch {
const std::vector<TestCaseInfo>& allTests = getCurrentContext().getTestCaseRegistry().getAllTests(); const std::vector<TestCaseInfo>& allTests = getCurrentContext().getTestCaseRegistry().getAllTests();
for( std::size_t i=0; i < allTests.size(); ++i ) { for( std::size_t i=0; i < allTests.size(); ++i ) {
if( runHiddenTests || !allTests[i].isHidden() ) if( runHiddenTests || !allTests[i].isHidden() )
{
if( aborting() ) {
m_reporter->Aborted();
break;
}
runTest( allTests[i] ); runTest( allTests[i] );
} }
} }
}
virtual std::size_t runMatching( const std::string& rawTestSpec ) { virtual std::size_t runMatching( const std::string& rawTestSpec ) {
TestSpec testSpec( rawTestSpec ); TestSpec testSpec( rawTestSpec );
@ -89,6 +95,10 @@ namespace Catch {
std::size_t testsRun = 0; std::size_t testsRun = 0;
for( std::size_t i=0; i < allTests.size(); ++i ) { for( std::size_t i=0; i < allTests.size(); ++i ) {
if( testSpec.matches( allTests[i].getName() ) ) { if( testSpec.matches( allTests[i].getName() ) ) {
if( aborting() ) {
m_reporter->Aborted();
break;
}
runTest( allTests[i] ); runTest( allTests[i] );
testsRun++; testsRun++;
} }
@ -108,14 +118,14 @@ namespace Catch {
do { do {
do { do {
m_reporter->StartGroup( "test case run" ); // m_reporter->StartGroup( "test case run" );
m_currentResult.setLineInfo( m_runningTest->getTestCaseInfo().getLineInfo() ); m_currentResult.setLineInfo( m_runningTest->getTestCaseInfo().getLineInfo() );
runCurrentTest( redirectedCout, redirectedCerr ); runCurrentTest( redirectedCout, redirectedCerr );
m_reporter->EndGroup( "test case run", m_totals.delta( prevTotals ) ); // m_reporter->EndGroup( "test case run", m_totals.delta( prevTotals ) );
} }
while( m_runningTest->hasUntestedSections() ); while( m_runningTest->hasUntestedSections() && !aborting() );
} }
while( getCurrentContext().advanceGeneratorsForCurrentTest() ); while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
delete m_runningTest; delete m_runningTest;
m_runningTest = NULL; m_runningTest = NULL;
@ -219,17 +229,26 @@ namespace Catch {
private: private:
bool aborting() const {
return m_totals.assertions.failed == m_config.getCutoff();
}
ResultAction::Value actOnCurrentResult() { ResultAction::Value actOnCurrentResult() {
testEnded( m_currentResult ); testEnded( m_currentResult );
m_lastResult = m_currentResult; m_lastResult = m_currentResult;
m_currentResult = ResultInfoBuilder(); m_currentResult = ResultInfoBuilder();
if( m_lastResult.ok() )
return ResultAction::None; ResultAction::Value action = ResultAction::None;
else if( shouldDebugBreak() )
return ResultAction::DebugFailed; if( !m_lastResult.ok() ) {
else action = ResultAction::Failed;
return ResultAction::Failed; if( shouldDebugBreak() )
action = (ResultAction::Value)( action | ResultAction::Debug );
if( aborting() )
action = (ResultAction::Value)( action | ResultAction::Abort );
}
return action;
} }
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {

View File

@ -57,7 +57,8 @@ namespace Catch {
public: public:
BasicReporter( const IReporterConfig& config ) BasicReporter( const IReporterConfig& config )
: m_config( config ), : m_config( config ),
m_firstSectionInTestCase( true ) m_firstSectionInTestCase( true ),
m_aborted( false )
{} {}
static std::string getDescription() { static std::string getDescription() {
@ -66,29 +67,29 @@ namespace Catch {
private: private:
void ReportCounts( const std::string& label, const Counts& counts ) { void ReportCounts( const std::string& label, const Counts& counts, const std::string& allPrefix = "All " ) {
if( counts.passed ) if( counts.passed )
m_config.stream() << counts.failed << " of " << counts.total() << " " << label << "s failed"; m_config.stream() << counts.failed << " of " << counts.total() << " " << label << "s failed";
else else
m_config.stream() << ( counts.failed > 1 ? "All " : "" ) << pluralise( counts.failed, label ) << " failed"; m_config.stream() << ( counts.failed > 1 ? allPrefix : "" ) << pluralise( counts.failed, label ) << " failed";
} }
void ReportCounts( const Totals& totals ) { void ReportCounts( const Totals& totals, const std::string& allPrefix = "All " ) {
if( totals.assertions.total() == 0 ) { if( totals.assertions.total() == 0 ) {
m_config.stream() << "No tests ran"; m_config.stream() << "No tests ran";
} }
else if( totals.assertions.failed ) { else if( totals.assertions.failed ) {
TextColour colour( TextColour::ResultError ); TextColour colour( TextColour::ResultError );
ReportCounts( "test case", totals.testCases ); ReportCounts( "test case", totals.testCases, allPrefix );
if( totals.testCases.failed > 0 ) { if( totals.testCases.failed > 0 ) {
m_config.stream() << " ("; m_config.stream() << " (";
ReportCounts( "assertion", totals.assertions ); ReportCounts( "assertion", totals.assertions, allPrefix );
m_config.stream() << ")"; m_config.stream() << ")";
} }
} }
else { else {
TextColour colour( TextColour::ResultSuccess ); TextColour colour( TextColour::ResultSuccess );
m_config.stream() << "All tests passed (" m_config.stream() << allPrefix << "tests passed ("
<< pluralise( totals.assertions.passed, "assertion" ) << " in " << pluralise( totals.assertions.passed, "assertion" ) << " in "
<< pluralise( totals.testCases.passed, "test case" ) << ")"; << pluralise( totals.testCases.passed, "test case" ) << ")";
} }
@ -104,10 +105,20 @@ namespace Catch {
m_testingSpan = SpanInfo(); m_testingSpan = SpanInfo();
} }
virtual void Aborted() {
m_aborted = true;
}
virtual void EndTesting( const Totals& totals ) { virtual void EndTesting( const Totals& totals ) {
// Output the overall test results even if "Started Testing" was not emitted // Output the overall test results even if "Started Testing" was not emitted
if( m_aborted ) {
m_config.stream() << "\n[Testing aborted. ";
ReportCounts( totals, "The first " );
}
else {
m_config.stream() << "\n[Testing completed. "; m_config.stream() << "\n[Testing completed. ";
ReportCounts( totals ); ReportCounts( totals );
}
m_config.stream() << "]\n" << std::endl; m_config.stream() << "]\n" << std::endl;
} }
@ -314,6 +325,7 @@ namespace Catch {
SpanInfo m_groupSpan; SpanInfo m_groupSpan;
SpanInfo m_testSpan; SpanInfo m_testSpan;
std::vector<SpanInfo> m_sectionSpans; std::vector<SpanInfo> m_sectionSpans;
bool m_aborted;
}; };
} // end namespace Catch } // end namespace Catch

View File

@ -146,6 +146,10 @@ namespace Catch {
m_stdErr << stdErr << "\n"; m_stdErr << stdErr << "\n";
} }
virtual void Aborted() {
// !TBD
}
virtual void EndTesting( const Totals& ) { virtual void EndTesting( const Totals& ) {
std::ostream& str = m_config.stream(); std::ostream& str = m_config.stream();
{ {

View File

@ -123,6 +123,10 @@ namespace Catch {
m_xml.endElement(); m_xml.endElement();
} }
virtual void Aborted() {
// !TBD
}
virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) { virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) {
m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess ); m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess );
m_xml.endElement(); m_xml.endElement();

View File

@ -98,29 +98,21 @@ TEST_CASE( "Sections/nested3", "nested SECTION tests" )
CHECK( runner.getLog() == CHECK( runner.getLog() ==
"\\[tc] ./Sections/nested/a/b\n" "\\[tc] ./Sections/nested/a/b\n"
" \\[g] test case run\n"
" \\ [s] c\n" " \\ [s] c\n"
" \\ [s] d (leaf)\n" " \\ [s] d (leaf)\n"
" / [s] d (leaf)\n" " / [s] d (leaf)\n"
" / [s] c\n" " / [s] c\n"
" /[g] test case run\n"
" \\[g] test case run\n"
" \\ [s] c\n" " \\ [s] c\n"
" \\ [s] e (leaf)\n" " \\ [s] e (leaf)\n"
" / [s] e (leaf)\n" " / [s] e (leaf)\n"
" / [s] c\n" " / [s] c\n"
" /[g] test case run\n"
" \\[g] test case run\n"
" \\ [s] c\n" " \\ [s] c\n"
" / [s] c\n" " / [s] c\n"
" /[g] test case run\n"
" \\[g] test case run\n"
" \\ [s] f (leaf)\n" " \\ [s] f (leaf)\n"
" / [s] f (leaf)\n" " / [s] f (leaf)\n"
" /[g] test case run\n"
"/[tc] ./Sections/nested/a/b\n" ); "/[tc] ./Sections/nested/a/b\n" );

View File

@ -74,6 +74,7 @@ TEST_CASE( "selftest/parser", "" ) {
CHECK( config.getTestSpecs().empty() ); CHECK( config.getTestSpecs().empty() );
CHECK( config.shouldDebugBreak() == false ); CHECK( config.shouldDebugBreak() == false );
CHECK( config.showHelp() == false ); CHECK( config.showHelp() == false );
CHECK( config.getCutoff() == -1 );
CHECK( dynamic_cast<Catch::BasicReporter*>( config.getReporter().get() ) ); CHECK( dynamic_cast<Catch::BasicReporter*>( config.getReporter().get() ) );
} }
@ -200,4 +201,39 @@ TEST_CASE( "selftest/parser", "" ) {
REQUIRE_THAT( config.getMessage(), Contains( "not accept" ) ); REQUIRE_THAT( config.getMessage(), Contains( "not accept" ) );
} }
} }
SECTION( "cutoff", "" ) {
SECTION( "-c", "" ) {
const char* argv[] = { "test", "-c" };
Catch::Config config;
CHECK( parseIntoConfig( argv, config ) );
REQUIRE( config.getCutoff() == 1 );
}
SECTION( "-c/2", "" ) {
const char* argv[] = { "test", "-c", "2" };
Catch::Config config;
CHECK( parseIntoConfig( argv, config ) );
REQUIRE( config.getCutoff() == 2 );
}
SECTION( "-c/error", "cutoff only takes one argument" ) {
const char* argv[] = { "test", "-c", "1", "2" };
Catch::Config config;
CHECK( parseIntoConfig( argv, config ) == false );
REQUIRE_THAT( config.getMessage(), Contains( "accepts" ) );
}
}
SECTION( "combinations", "" ) {
SECTION( "-c -b", "" ) {
const char* argv[] = { "test", "-c", "-b" };
Catch::Config config;
CHECK( parseIntoConfig( argv, config ) );
REQUIRE( config.getCutoff() == 1 );
REQUIRE( config.shouldDebugBreak() );
}
}
} }

View File

@ -77,6 +77,8 @@ namespace Catch {
openLabel( recordTestCases, testInfo.getName() ); openLabel( recordTestCases, testInfo.getName() );
} }
virtual void Aborted(){}
virtual void EndTestCase( const TestCaseInfo& testInfo, virtual void EndTestCase( const TestCaseInfo& testInfo,
const Totals&, const Totals&,
const std::string&, const std::string&,

View File

@ -1,5 +1,5 @@
/* /*
* Generated: 2012-05-31 19:40:06.141562 * Generated: 2012-06-01 19:40:15.883536
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@ -424,6 +424,7 @@ namespace Catch
virtual void StartSection( const std::string& sectionName, const std::string& description ) = 0; virtual void StartSection( const std::string& sectionName, const std::string& description ) = 0;
virtual void EndSection( const std::string& sectionName, const Counts& assertions ) = 0; virtual void EndSection( const std::string& sectionName, const Counts& assertions ) = 0;
virtual void StartTestCase( const TestCaseInfo& testInfo ) = 0; virtual void StartTestCase( const TestCaseInfo& testInfo ) = 0;
virtual void Aborted() = 0;
virtual void EndTestCase( const TestCaseInfo& testInfo, const Totals& totals, const std::string& stdOut, const std::string& stdErr ) = 0; virtual void EndTestCase( const TestCaseInfo& testInfo, const Totals& totals, const std::string& stdOut, const std::string& stdErr ) = 0;
virtual void Result( const ResultInfo& result ) = 0; virtual void Result( const ResultInfo& result ) = 0;
}; };
@ -769,8 +770,8 @@ struct ResultWas { enum OfType {
struct ResultAction { enum Value { struct ResultAction { enum Value {
None, None,
Failed = 1, // Failure - but no debug break if Debug bit not set Failed = 1, // Failure - but no debug break if Debug bit not set
DebugFailed = 3 // Indicates that the debugger should break, if possible Debug = 2, // If this bit is set, invoke the debugger
Abort = 4 // Test run should abort
}; }; }; };
} }
@ -1469,7 +1470,8 @@ inline bool isTrue( bool value ){ return value; }
#define INTERNAL_CATCH_ACCEPT_EXPR( expr, stopOnFailure, originalExpr ) \ #define INTERNAL_CATCH_ACCEPT_EXPR( expr, stopOnFailure, originalExpr ) \
if( Catch::ResultAction::Value internal_catch_action = Catch::getCurrentContext().getResultCapture().acceptExpression( expr ) ) \ if( Catch::ResultAction::Value internal_catch_action = Catch::getCurrentContext().getResultCapture().acceptExpression( expr ) ) \
{ \ { \
if( internal_catch_action == Catch::ResultAction::DebugFailed ) BreakIntoDebugger(); \ if( internal_catch_action & Catch::ResultAction::Debug ) BreakIntoDebugger(); \
if( internal_catch_action & Catch::ResultAction::Abort ) throw Catch::TestFailureException(); \
if( Catch::isTrue( stopOnFailure ) ) throw Catch::TestFailureException(); \ if( Catch::isTrue( stopOnFailure ) ) throw Catch::TestFailureException(); \
if( Catch::isTrue( false ) ){ bool this_is_here_to_invoke_warnings = ( originalExpr ); Catch::isTrue( this_is_here_to_invoke_warnings ); } \ if( Catch::isTrue( false ) ){ bool this_is_here_to_invoke_warnings = ( originalExpr ); Catch::isTrue( this_is_here_to_invoke_warnings ); } \
} }
@ -2401,7 +2403,8 @@ namespace Catch {
m_showHelp( false ), m_showHelp( false ),
m_streambuf( NULL ), m_streambuf( NULL ),
m_os( std::cout.rdbuf() ), m_os( std::cout.rdbuf() ),
m_includeWhichResults( Include::FailedOnly ) m_includeWhichResults( Include::FailedOnly ),
m_cutoff( -1 )
{} {}
~Config() { ~Config() {
@ -2516,6 +2519,14 @@ namespace Catch {
return m_includeWhichResults == Include::SuccessfulResults; return m_includeWhichResults == Include::SuccessfulResults;
} }
int getCutoff() const {
return m_cutoff;
}
void setCutoff( int cutoff ) {
m_cutoff = cutoff;
}
private: private:
Ptr<IReporter> m_reporter; Ptr<IReporter> m_reporter;
std::string m_filename; std::string m_filename;
@ -2528,6 +2539,7 @@ namespace Catch {
mutable std::ostream m_os; mutable std::ostream m_os;
Include::WhichResults m_includeWhichResults; Include::WhichResults m_includeWhichResults;
std::string m_name; std::string m_name;
int m_cutoff;
}; };
struct NewConfig { struct NewConfig {
@ -2786,9 +2798,15 @@ namespace Catch {
const std::vector<TestCaseInfo>& allTests = getCurrentContext().getTestCaseRegistry().getAllTests(); const std::vector<TestCaseInfo>& allTests = getCurrentContext().getTestCaseRegistry().getAllTests();
for( std::size_t i=0; i < allTests.size(); ++i ) { for( std::size_t i=0; i < allTests.size(); ++i ) {
if( runHiddenTests || !allTests[i].isHidden() ) if( runHiddenTests || !allTests[i].isHidden() )
{
if( aborting() ) {
m_reporter->Aborted();
break;
}
runTest( allTests[i] ); runTest( allTests[i] );
} }
} }
}
virtual std::size_t runMatching( const std::string& rawTestSpec ) { virtual std::size_t runMatching( const std::string& rawTestSpec ) {
TestSpec testSpec( rawTestSpec ); TestSpec testSpec( rawTestSpec );
@ -2797,6 +2815,10 @@ namespace Catch {
std::size_t testsRun = 0; std::size_t testsRun = 0;
for( std::size_t i=0; i < allTests.size(); ++i ) { for( std::size_t i=0; i < allTests.size(); ++i ) {
if( testSpec.matches( allTests[i].getName() ) ) { if( testSpec.matches( allTests[i].getName() ) ) {
if( aborting() ) {
m_reporter->Aborted();
break;
}
runTest( allTests[i] ); runTest( allTests[i] );
testsRun++; testsRun++;
} }
@ -2816,14 +2838,14 @@ namespace Catch {
do { do {
do { do {
m_reporter->StartGroup( "test case run" ); // m_reporter->StartGroup( "test case run" );
m_currentResult.setLineInfo( m_runningTest->getTestCaseInfo().getLineInfo() ); m_currentResult.setLineInfo( m_runningTest->getTestCaseInfo().getLineInfo() );
runCurrentTest( redirectedCout, redirectedCerr ); runCurrentTest( redirectedCout, redirectedCerr );
m_reporter->EndGroup( "test case run", m_totals.delta( prevTotals ) ); // m_reporter->EndGroup( "test case run", m_totals.delta( prevTotals ) );
} }
while( m_runningTest->hasUntestedSections() ); while( m_runningTest->hasUntestedSections() && !aborting() );
} }
while( getCurrentContext().advanceGeneratorsForCurrentTest() ); while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
delete m_runningTest; delete m_runningTest;
m_runningTest = NULL; m_runningTest = NULL;
@ -2927,17 +2949,26 @@ namespace Catch {
private: private:
bool aborting() const {
return m_totals.assertions.failed == m_config.getCutoff();
}
ResultAction::Value actOnCurrentResult() { ResultAction::Value actOnCurrentResult() {
testEnded( m_currentResult ); testEnded( m_currentResult );
m_lastResult = m_currentResult; m_lastResult = m_currentResult;
m_currentResult = ResultInfoBuilder(); m_currentResult = ResultInfoBuilder();
if( m_lastResult.ok() )
return ResultAction::None; ResultAction::Value action = ResultAction::None;
else if( shouldDebugBreak() )
return ResultAction::DebugFailed; if( !m_lastResult.ok() ) {
else action = ResultAction::Failed;
return ResultAction::Failed; if( shouldDebugBreak() )
action = (ResultAction::Value)( action | ResultAction::Debug );
if( aborting() )
action = (ResultAction::Value)( action | ResultAction::Abort );
}
return action;
} }
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
@ -3565,6 +3596,19 @@ namespace Catch {
throw std::domain_error( cmd.name() + " does not accept arguments" ); throw std::domain_error( cmd.name() + " does not accept arguments" );
config.setShowHelp( true ); config.setShowHelp( true );
} }
if( Command cmd = parser.find( "-c", "--cutoff" ) ) {
if( cmd.argsCount() > 1 )
throw std::domain_error( cmd.name() + " only accepts 0-1 arguments" );
int threshold = 1;
if( cmd.argsCount() == 1 )
{
std::stringstream ss;
ss << cmd[0];
ss >> threshold;
}
config.setCutoff( threshold );
}
} }
catch( std::exception& ex ) { catch( std::exception& ex ) {
config.setError( ex.what() ); config.setError( ex.what() );
@ -3694,7 +3738,8 @@ namespace Catch {
public: public:
BasicReporter( const IReporterConfig& config ) BasicReporter( const IReporterConfig& config )
: m_config( config ), : m_config( config ),
m_firstSectionInTestCase( true ) m_firstSectionInTestCase( true ),
m_aborted( false )
{} {}
static std::string getDescription() { static std::string getDescription() {
@ -3703,29 +3748,29 @@ namespace Catch {
private: private:
void ReportCounts( const std::string& label, const Counts& counts ) { void ReportCounts( const std::string& label, const Counts& counts, const std::string& allPrefix = "All " ) {
if( counts.passed ) if( counts.passed )
m_config.stream() << counts.failed << " of " << counts.total() << " " << label << "s failed"; m_config.stream() << counts.failed << " of " << counts.total() << " " << label << "s failed";
else else
m_config.stream() << ( counts.failed > 1 ? "All " : "" ) << pluralise( counts.failed, label ) << " failed"; m_config.stream() << ( counts.failed > 1 ? allPrefix : "" ) << pluralise( counts.failed, label ) << " failed";
} }
void ReportCounts( const Totals& totals ) { void ReportCounts( const Totals& totals, const std::string& allPrefix = "All " ) {
if( totals.assertions.total() == 0 ) { if( totals.assertions.total() == 0 ) {
m_config.stream() << "No tests ran"; m_config.stream() << "No tests ran";
} }
else if( totals.assertions.failed ) { else if( totals.assertions.failed ) {
TextColour colour( TextColour::ResultError ); TextColour colour( TextColour::ResultError );
ReportCounts( "test case", totals.testCases ); ReportCounts( "test case", totals.testCases, allPrefix );
if( totals.testCases.failed > 0 ) { if( totals.testCases.failed > 0 ) {
m_config.stream() << " ("; m_config.stream() << " (";
ReportCounts( "assertion", totals.assertions ); ReportCounts( "assertion", totals.assertions, allPrefix );
m_config.stream() << ")"; m_config.stream() << ")";
} }
} }
else { else {
TextColour colour( TextColour::ResultSuccess ); TextColour colour( TextColour::ResultSuccess );
m_config.stream() << "All tests passed (" m_config.stream() << allPrefix << "tests passed ("
<< pluralise( totals.assertions.passed, "assertion" ) << " in " << pluralise( totals.assertions.passed, "assertion" ) << " in "
<< pluralise( totals.testCases.passed, "test case" ) << ")"; << pluralise( totals.testCases.passed, "test case" ) << ")";
} }
@ -3741,10 +3786,20 @@ namespace Catch {
m_testingSpan = SpanInfo(); m_testingSpan = SpanInfo();
} }
virtual void Aborted() {
m_aborted = true;
}
virtual void EndTesting( const Totals& totals ) { virtual void EndTesting( const Totals& totals ) {
// Output the overall test results even if "Started Testing" was not emitted // Output the overall test results even if "Started Testing" was not emitted
if( m_aborted ) {
m_config.stream() << "\n[Testing aborted. ";
ReportCounts( totals, "The first " );
}
else {
m_config.stream() << "\n[Testing completed. "; m_config.stream() << "\n[Testing completed. ";
ReportCounts( totals ); ReportCounts( totals );
}
m_config.stream() << "]\n" << std::endl; m_config.stream() << "]\n" << std::endl;
} }
@ -3951,6 +4006,7 @@ namespace Catch {
SpanInfo m_groupSpan; SpanInfo m_groupSpan;
SpanInfo m_testSpan; SpanInfo m_testSpan;
std::vector<SpanInfo> m_sectionSpans; std::vector<SpanInfo> m_sectionSpans;
bool m_aborted;
}; };
} // end namespace Catch } // end namespace Catch
@ -4268,6 +4324,10 @@ namespace Catch {
m_xml.endElement(); m_xml.endElement();
} }
virtual void Aborted() {
// !TBD
}
virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) { virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) {
m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess ); m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess );
m_xml.endElement(); m_xml.endElement();
@ -4416,6 +4476,10 @@ namespace Catch {
m_stdErr << stdErr << "\n"; m_stdErr << stdErr << "\n";
} }
virtual void Aborted() {
// !TBD
}
virtual void EndTesting( const Totals& ) { virtual void EndTesting( const Totals& ) {
std::ostream& str = m_config.stream(); std::ostream& str = m_config.stream();
{ {
@ -4557,6 +4621,7 @@ namespace Catch {
<< "\t-s, --success\n" << "\t-s, --success\n"
<< "\t-b, --break\n" << "\t-b, --break\n"
<< "\t-n, --name <name>\n\n" << "\t-n, --name <name>\n\n"
<< "\t-c, --cutoff [#]\n\n"
<< "For more detail usage please see: https://github.com/philsquared/Catch/wiki/Command-line" << std::endl; << "For more detail usage please see: https://github.com/philsquared/Catch/wiki/Command-line" << std::endl;
} }
inline void showHelp( std::string exeName ) { inline void showHelp( std::string exeName ) {