Added CATCH_ENFORCE (and updated CATCH_INTERNAL_ERROR to be stream based)

replaced all ad-hoc exceptions (where appropriate) with CATCH_ENFORCE or CATCH_INTERNAL_ERROR - no explicit ostringstreams.
This commit is contained in:
Phil Nash 2017-05-05 15:42:57 +01:00
parent 687437fcd1
commit 4c5af2089a
13 changed files with 65 additions and 97 deletions

View File

@ -23,12 +23,10 @@
namespace Catch { namespace Catch {
IStreamingReporterPtr createReporter( std::string const& reporterName, IConfigPtr const& config ) { IStreamingReporterPtr createReporter( std::string const& reporterName, IConfigPtr const& config ) {
if( auto reporter = getRegistryHub().getReporterRegistry().create( reporterName, config ) ) auto reporter = getRegistryHub().getReporterRegistry().create( reporterName, config );
return reporter; CATCH_ENFORCE( reporter, "No reporter registered with name: '" << reporterName << "'" );
std::ostringstream oss; return reporter;
oss << "No reporter registered with name: '" << reporterName << "'";
throw std::domain_error( oss.str() );
} }
IStreamingReporterPtr makeReporter( std::shared_ptr<Config> const& config ) { IStreamingReporterPtr makeReporter( std::shared_ptr<Config> const& config ) {
@ -105,11 +103,8 @@ namespace Catch {
Session() Session()
: m_cli( makeCommandLineParser() ) { : m_cli( makeCommandLineParser() ) {
if( alreadyInstantiated ) { if( alreadyInstantiated )
std::string msg = "Only one instance of Catch::Session can ever be used"; CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" );
Catch::cerr() << msg << std::endl;
throw std::logic_error( msg );
}
alreadyInstantiated = true; alreadyInstantiated = true;
} }
~Session() { ~Session() {

View File

@ -19,19 +19,16 @@ namespace Catch {
inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
inline void abortAfterX( ConfigData& config, int x ) { inline void abortAfterX( ConfigData& config, int x ) {
if( x < 1 ) CATCH_ENFORCE( x >=1, "Value after -x or --abortAfter must be greater than zero" );
throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
config.abortAfter = x; config.abortAfter = x;
} }
inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } inline void addTestOrTags( ConfigData& config, std::string const& testSpec ) { config.testsOrTags.push_back( testSpec ); }
inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addReporterName( ConfigData& config, std::string const& reporterName ) { config.reporterNames.push_back( reporterName ); }
inline void addWarning( ConfigData& config, std::string const& _warning ) { inline void addWarning( ConfigData& config, std::string const& warning ) {
if( _warning == "NoAssertions" ) CATCH_ENFORCE( warning == "NoAssertions", "Unrecognised warning: '" << warning << "'" );
config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions ); config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
else
throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
} }
inline void setOrder( ConfigData& config, std::string const& order ) { inline void setOrder( ConfigData& config, std::string const& order ) {
if( startsWith( "declared", order ) ) if( startsWith( "declared", order ) )
@ -41,7 +38,7 @@ namespace Catch {
else if( startsWith( "random", order ) ) else if( startsWith( "random", order ) )
config.runOrder = RunTests::InRandomOrder; config.runOrder = RunTests::InRandomOrder;
else else
throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); CATCH_ENFORCE( false, "Unrecognised ordering: '" << order << '\'' );
} }
inline void setRngSeed( ConfigData& config, std::string const& seed ) { inline void setRngSeed( ConfigData& config, std::string const& seed ) {
if( seed == "time" ) { if( seed == "time" ) {
@ -51,8 +48,7 @@ namespace Catch {
std::stringstream ss; std::stringstream ss;
ss << seed; ss << seed;
ss >> config.rngSeed; ss >> config.rngSeed;
if( ss.fail() ) CATCH_ENFORCE( !ss.fail(), "Argument to --rng-seed should be the word 'time' or a number" );
throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
} }
} }
inline void setVerbosity( ConfigData& config, int level ) { inline void setVerbosity( ConfigData& config, int level ) {
@ -74,15 +70,14 @@ namespace Catch {
else if( mode == "auto" ) else if( mode == "auto" )
config.useColour = UseColour::Auto; config.useColour = UseColour::Auto;
else else
throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); CATCH_ENFORCE( false, "colour mode must be one of: auto, yes or no" );
} }
inline void forceColour( ConfigData& config ) { inline void forceColour( ConfigData& config ) {
config.useColour = UseColour::Yes; config.useColour = UseColour::Yes;
} }
inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
std::ifstream f( _filename.c_str() ); std::ifstream f( _filename.c_str() );
if( !f.is_open() ) CATCH_ENFORCE( f.is_open(), "Unable to load input file: '" << _filename << "'" );
throw std::domain_error( "Unable to load input file: " + _filename );
std::string line; std::string line;
while( std::getline( f, line ) ) { while( std::getline( f, line ) ) {

View File

@ -23,6 +23,7 @@
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
#include <exception>
namespace Catch { namespace Catch {
@ -99,8 +100,6 @@ namespace Catch {
inline bool alwaysTrue() { return true; } inline bool alwaysTrue() { return true; }
inline bool alwaysFalse() { return false; } inline bool alwaysFalse() { return false; }
void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
void seedRng( IConfig const& config ); void seedRng( IConfig const& config );
unsigned int rngSeed(); unsigned int rngSeed();
@ -117,10 +116,29 @@ namespace Catch {
T const& operator + ( T const& value, StreamEndStop ) { T const& operator + ( T const& value, StreamEndStop ) {
return value; return value;
} }
template<typename ExceptionT = std::domain_error>
class Error {
std::ostringstream m_oss;
public:
template<typename T>
auto operator <<( T const& value ) -> Error& {
m_oss << value;
return *this;
}
[[noreturn]]
void raise() {
throw ExceptionT( m_oss.str() );
}
};
} }
#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); #define CATCH_INTERNAL_ERROR( msg ) do{ ( ::Catch::Error<std::logic_error>() << CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg ).raise(); } while(false)
#define CATCH_ERROR( msg ) ( ::Catch::Error<>() << msg ).raise()
#define CATCH_ENFORCE( condition, msg ) do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
#endif // TWOBLUECUBES_CATCH_COMMON_H_INCLUDED #endif // TWOBLUECUBES_CATCH_COMMON_H_INCLUDED

View File

@ -106,13 +106,6 @@ namespace Catch {
#endif #endif
return os; return os;
} }
void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
std::ostringstream oss;
oss << locationInfo << ": Internal Catch error: '" << message << '\'';
if( alwaysTrue() )
throw std::logic_error( oss.str() );
}
} }
#endif // TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED

View File

@ -118,7 +118,7 @@ namespace Catch {
if( m_data.outputFilename == "%debug" ) if( m_data.outputFilename == "%debug" )
return new DebugOutStream(); return new DebugOutStream();
else else
throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" );
} }
else else
return new FileStream( m_data.outputFilename ); return new FileStream( m_data.outputFilename );

View File

@ -73,7 +73,7 @@ namespace {
case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
case Colour::Bright: throw std::logic_error( "not a colour" ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
} }
} }
@ -134,7 +134,7 @@ namespace {
case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightGreen: return setColour( "[1;32m" );
case Colour::BrightWhite: return setColour( "[1;37m" ); case Colour::BrightWhite: return setColour( "[1;37m" );
case Colour::Bright: throw std::logic_error( "not a colour" ); case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
} }
} }
static IColourImpl* instance() { static IColourImpl* instance() {

View File

@ -371,7 +371,7 @@ namespace Catch {
if( IResultCapture* capture = getCurrentContext().getResultCapture() ) if( IResultCapture* capture = getCurrentContext().getResultCapture() )
return *capture; return *capture;
else else
throw std::logic_error( "No result capture instance" ); CATCH_INTERNAL_ERROR( "No result capture instance" );
} }
} // end namespace Catch } // end namespace Catch

View File

@ -59,11 +59,7 @@ namespace Catch {
FileStream::FileStream( std::string const& filename ) { FileStream::FileStream( std::string const& filename ) {
m_ofs.open( filename.c_str() ); m_ofs.open( filename.c_str() );
if( m_ofs.fail() ) { CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
std::ostringstream oss;
oss << "Unable to open file: '" << filename << '\'';
throw std::domain_error( oss.str() );
}
} }
std::ostream& FileStream::stream() const { std::ostream& FileStream::stream() const {

View File

@ -40,24 +40,13 @@ namespace Catch {
void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { CATCH_ENFORCE( startsWith( alias, "[@" ) && endsWith( alias, ']' ),
std::ostringstream oss; "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
oss << Colour( Colour::Red )
<< "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" CATCH_ENFORCE( m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second,
<< Colour( Colour::FileName ) "error: tag alias, '" << alias << "' already registered.\n"
<< lineInfo << '\n'; << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
throw std::domain_error( oss.str().c_str() ); << "\tRedefined at: " << lineInfo );
}
if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
std::ostringstream oss;
oss << Colour( Colour::Red )
<< "error: tag alias, \"" << alias << "\" already registered.\n"
<< "\tFirst seen at "
<< Colour( Colour::Red ) << find(alias)->lineInfo << '\n'
<< Colour( Colour::Red ) << "\tRedefined at "
<< Colour( Colour::FileName) << lineInfo << '\n';
throw std::domain_error( oss.str().c_str() );
}
} }
ITagAliasRegistry::~ITagAliasRegistry() {} ITagAliasRegistry::~ITagAliasRegistry() {}

View File

@ -37,15 +37,10 @@ namespace Catch {
return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
} }
inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
if( isReservedTag( tag ) ) { CATCH_ENFORCE( !isReservedTag( tag ),
std::ostringstream ss; "Tag name: [" << tag << "] is not allowed.\n"
ss << Colour(Colour::Red)
<< "Tag name [" << tag << "] not allowed.\n"
<< "Tag names starting with non alpha-numeric characters are reserved\n" << "Tag names starting with non alpha-numeric characters are reserved\n"
<< Colour(Colour::FileName) << _lineInfo );
<< _lineInfo << '\n';
throw std::runtime_error(ss.str());
}
} }
TestCase makeTestCase( ITestCase* _testCase, TestCase makeTestCase( ITestCase* _testCase,

View File

@ -62,18 +62,12 @@ namespace Catch {
void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
std::set<TestCase> seenFunctions; std::set<TestCase> seenFunctions;
for( auto const function : functions ) { for( auto const& function : functions ) {
std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( function ); auto prev = seenFunctions.insert( function );
if( !prev.second ) { CATCH_ENFORCE( prev.second,
std::ostringstream ss; "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
<< "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
ss << Colour( Colour::Red ) << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
<< "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
<< "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
<< "\tRedefined at " << function.getTestCaseInfo().lineInfo << std::endl;
throw std::runtime_error(ss.str());
}
} }
} }

View File

@ -202,11 +202,6 @@ namespace TestCaseTracking {
m_ctx.currentTracker().close(); m_ctx.currentTracker().close();
switch( m_runState ) { switch( m_runState ) {
case NotStarted:
case CompletedSuccessfully:
case Failed:
throw std::logic_error( "Illogical state" );
case NeedsAnotherRun: case NeedsAnotherRun:
break;; break;;
@ -218,8 +213,13 @@ namespace TestCaseTracking {
m_runState = CompletedSuccessfully; m_runState = CompletedSuccessfully;
break; break;
case NotStarted:
case CompletedSuccessfully:
case Failed:
CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
default: default:
throw std::logic_error( "Unexpected state" ); CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
} }
moveToParent(); moveToParent();
m_ctx.completeCycle(); m_ctx.completeCycle();

View File

@ -49,16 +49,9 @@ namespace Catch
return startsWith( adjustCase( str ), m_pattern ); return startsWith( adjustCase( str ), m_pattern );
case WildcardAtBothEnds: case WildcardAtBothEnds:
return contains( adjustCase( str ), m_pattern ); return contains( adjustCase( str ), m_pattern );
default:
CATCH_INTERNAL_ERROR( "Unknown enum" );
} }
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
#endif
throw std::logic_error( "Unknown enum" );
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} }
private: private:
std::string adjustCase( std::string const& str ) const { std::string adjustCase( std::string const& str ) const {