Added StringMaker (for partially specialising string conversions), extended BDD macros and moved file/line info to top of message.

Re-enable ANSI colour by default - hopefully properly excluding Windows this time
This commit is contained in:
Phil Nash 2013-03-04 12:19:15 +01:00
parent ead139e094
commit 767f1588dc
14 changed files with 952 additions and 872 deletions

2
README
View File

@ -1,4 +1,4 @@
CATCH v0.9 build 20 (integration branch) CATCH v0.9 build 21 (integration branch)
--------------------------------------------- ---------------------------------------------
CATCH is an automated test framework for C, C++ and Objective-C. CATCH is an automated test framework for C, C++ and Objective-C.

View File

@ -17,6 +17,8 @@ versionPath = os.path.join( rootPath, "internal/catch_version.hpp" )
readmePath = os.path.join( catchPath, "README" ) readmePath = os.path.join( catchPath, "README" )
#outputPath = os.path.join( catchPath, 'single_include/catch.hpp' ) #outputPath = os.path.join( catchPath, 'single_include/catch.hpp' )
bumpVersion = len(sys.argv) < 2 or sys.argv[1] <> "nobump"
def parseFile( path, filename ): def parseFile( path, filename ):
f = open( path + filename, 'r' ) f = open( path + filename, 'r' )
blanks = 0 blanks = 0
@ -86,6 +88,7 @@ class Version:
def generateSingleInclude(): def generateSingleInclude():
v = Version() v = Version()
if bumpVersion:
v.incrementBuildNumber() v.incrementBuildNumber()
v.updateVersionFile() v.updateVersionFile()
v.updateReadmeFile() v.updateReadmeFile()

View File

@ -10,6 +10,10 @@
#include "catch_console_colour.hpp" #include "catch_console_colour.hpp"
#if !defined(CATCH_CONFIG_USE_ANSI_COLOUR_CODES) && !defined(CATCH_PLATFORM_WINDOWS)
#define CATCH_CONFIG_USE_ANSI_COLOUR_CODES 1
#endif
#if defined( CATCH_CONFIG_USE_ANSI_COLOUR_CODES ) #if defined( CATCH_CONFIG_USE_ANSI_COLOUR_CODES )
#include <unistd.h> #include <unistd.h>
@ -31,8 +35,15 @@ namespace Catch {
namespace { const char colourEscape = '\033'; } namespace { const char colourEscape = '\033'; }
inline bool shouldUseColour() {
static bool s_shouldUseColour
= CATCH_CONFIG_USE_ANSI_COLOUR_CODES != 0 &&
isatty( fileno(stdout) ) &&
!isDebuggerActive();
return s_shouldUseColour;
}
void TextColour::set( Colours colour ) { void TextColour::set( Colours colour ) {
if( isatty( fileno(stdout) ) && !isDebuggerActive() ) { if( shouldUseColour() ) {
switch( colour ) { switch( colour ) {
case TextColour::FileName: case TextColour::FileName:
std::cout << colourEscape << "[0m"; // white/ normal std::cout << colourEscape << "[0m"; // white/ normal

View File

@ -82,12 +82,10 @@ namespace Catch {
else if( m_exprComponents.op == "matches" ) else if( m_exprComponents.op == "matches" )
return m_exprComponents.lhs + " " + m_exprComponents.rhs; return m_exprComponents.lhs + " " + m_exprComponents.rhs;
else if( m_exprComponents.op != "!" ) { else if( m_exprComponents.op != "!" ) {
if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 30 ) if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 )
return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
else if( m_exprComponents.lhs.size() < 70 && m_exprComponents.rhs.size() < 70 )
return "\n\t" + m_exprComponents.lhs + "\n\t" + m_exprComponents.op + "\n\t" + m_exprComponents.rhs;
else else
return "\n" + m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs + "\n\n"; return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
} }
else else
return "{can't expand - use " + info.macroName + "_FALSE( " + info.capturedExpression.substr(1) + " ) instead of " + info.macroName + "( " + info.capturedExpression + " ) for better diagnostics}"; return "{can't expand - use " + info.macroName + "_FALSE( " + info.capturedExpression.substr(1) + " ) instead of " + info.macroName + "( " + info.capturedExpression + " ) for better diagnostics}";

View File

@ -216,7 +216,7 @@ namespace Catch {
missingAssertions = true; missingAssertions = true;
} }
m_runningTest->endSection( info.name ); m_runningTest->endSection( info.name, false );
m_reporter->sectionEnded( SectionStats( info, assertions, missingAssertions ) ); m_reporter->sectionEnded( SectionStats( info, assertions, missingAssertions ) );
m_messages.clear(); m_messages.clear();

View File

@ -78,12 +78,14 @@ namespace Catch {
return false; return false;
} }
void endSection( const std::string& ) { void endSection( const std::string&, bool stealth ) {
if( m_currentSection->ran() ) { if( m_currentSection->ran() ) {
if( !stealth )
m_runStatus = RanAtLeastOneSection; m_runStatus = RanAtLeastOneSection;
m_changed = true; m_changed = true;
} }
else if( m_runStatus == EncounteredASection ) { else if( m_runStatus == EncounteredASection ) {
if( !stealth )
m_runStatus = RanAtLeastOneSection; m_runStatus = RanAtLeastOneSection;
m_lastSectionToRun = m_currentSection; m_lastSectionToRun = m_currentSection;
} }

View File

@ -10,6 +10,8 @@
#include "catch_common.h" #include "catch_common.h"
#include <sstream> #include <sstream>
#include <iomanip>
#include <limits>
#ifdef __OBJC__ #ifdef __OBJC__
#include "catch_objc_arc.hpp" #include "catch_objc_arc.hpp"
@ -22,37 +24,53 @@ namespace Detail {
template<typename T> NonStreamable( const T& ){} template<typename T> NonStreamable( const T& ){}
}; };
// If the type does not have its own << overload for ostream then } // end namespace Detail
// this one will be used instead
inline std::ostream& operator << ( std::ostream& ss, NonStreamable ){
return ss << "{?}";
}
template<typename T> // If the type does not have its own << overload for ostream then
inline std::string makeString( const T& value ) { // this one will be used instead
inline std::ostream& operator << ( std::ostream& ss, Detail::NonStreamable ){
return ss << "{?}";
}
template<typename T>
struct StringMaker {
static std::string convert( T const& value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << value;
return oss.str(); return oss.str();
} }
};
template<typename T> template<typename T>
inline std::string makeString( T* p ) { struct StringMaker<T*> {
static std::string convert( T const* p ) {
if( !p ) if( !p )
return INTERNAL_CATCH_STRINGIFY( NULL ); return INTERNAL_CATCH_STRINGIFY( NULL );
std::ostringstream oss; std::ostringstream oss;
oss << p; oss << p;
return oss.str(); return oss.str();
} }
};
template<typename T> template<typename T>
inline std::string makeString( const T* p ) { struct StringMaker<std::vector<T> > {
if( !p ) static std::string convert( std::vector<T> const& v ) {
return INTERNAL_CATCH_STRINGIFY( NULL );
std::ostringstream oss; std::ostringstream oss;
oss << p; oss << "{ ";
for( std::size_t i = 0; i < v.size(); ++ i ) {
oss << v[i];
if( i < v.size() - 1 )
oss << ", ";
}
oss << " }";
return oss.str(); return oss.str();
} }
};
namespace Detail {
template<typename T>
inline std::string makeString( const T& value ) {
return StringMaker<T>::convert( value );
}
} // end namespace Detail } // end namespace Detail
/// \brief converts any type to a string /// \brief converts any type to a string
@ -64,7 +82,8 @@ namespace Detail {
/// to provide an ostream overload for. /// to provide an ostream overload for.
template<typename T> template<typename T>
std::string toString( const T& value ) { std::string toString( const T& value ) {
return Detail::makeString( value ); return StringMaker<T>::convert( value );
// return Detail::makeString( value );
} }
// Built in overloads // Built in overloads
@ -111,7 +130,8 @@ inline std::string toString( unsigned int value ) {
inline std::string toString( const double value ) { inline std::string toString( const double value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << std::setprecision (std::numeric_limits<double>::digits10 + 1)
<< value;
return oss.str(); return oss.str();
} }

View File

@ -13,7 +13,7 @@
namespace Catch { namespace Catch {
// These numbers are maintained by a script // These numbers are maintained by a script
Version libraryVersion( 0, 9, 20, "integration" ); Version libraryVersion( 0, 9, 21, "integration" );
} }
#endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED

View File

@ -174,13 +174,18 @@ namespace Catch {
} }
void print() const { void print() const {
printSourceInfo();
if( stats.totals.assertions.total() > 0 ) { if( stats.totals.assertions.total() > 0 ) {
if( result.isOk() )
stream << "\n";
printResultType(); printResultType();
printOriginalExpression(); printOriginalExpression();
printReconstructedExpression(); printReconstructedExpression();
} }
else {
stream << "\n";
}
printMessage(); printMessage();
printSourceInfo();
} }
private: private:
@ -222,7 +227,7 @@ namespace Catch {
} }
void printSourceInfo() const { void printSourceInfo() const {
TextColour colourGuard( TextColour::FileName ); TextColour colourGuard( TextColour::FileName );
stream << result.getSourceInfo() << ":\n"; stream << result.getSourceInfo() << ": ";
} }
static std::string wrapLongStrings( std::string const& _string ){ static std::string wrapLongStrings( std::string const& _string ){

View File

@ -101,3 +101,12 @@ TEST_CASE
REQUIRE( approx( d ) != 1.25 ); REQUIRE( approx( d ) != 1.25 );
} }
inline double divide( double a, double b ) {
return a/b;
}
TEST_CASE( "Approximate PI", "[Approx][PI]" )
{
REQUIRE( divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) );
REQUIRE( divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) );
}

File diff suppressed because it is too large Load Diff

View File

@ -580,7 +580,6 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++98"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++98";
CLANG_WARN__DUPLICATE_METHOD_MATCH = NO; CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;
GCC_PREPROCESSOR_DEFINITIONS = CATCH_CONFIG_USE_ANSI_COLOUR_CODES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = ( WARNING_CFLAGS = (
@ -596,7 +595,6 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++98"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++98";
CLANG_WARN__DUPLICATE_METHOD_MATCH = NO; CLANG_WARN__DUPLICATE_METHOD_MATCH = NO;
GCC_PREPROCESSOR_DEFINITIONS = CATCH_CONFIG_USE_ANSI_COLOUR_CODES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = ( WARNING_CFLAGS = (

View File

@ -14,11 +14,14 @@
// !TBD: story scenarios map to class based tests // !TBD: story scenarios map to class based tests
#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
#define GIVEN( desc ) SECTION( " Given: " desc, "" ) #define GIVEN( desc ) SECTION( "Given: " desc, "" )
#define WHEN( desc ) SECTION( " When: " desc, "" ) #define WHEN( desc ) SECTION( " When: " desc, "" )
#define AND_WHEN( desc ) SECTION( " And: " desc, "" )
#define THEN( desc ) SECTION( " Then: " desc, "" ) #define THEN( desc ) SECTION( " Then: " desc, "" )
#define AND_THEN( desc ) SECTION( " And: " desc, "" )
inline bool itDoesThis(){ return true; } inline bool itDoesThis(){ return true; }
inline bool itDoesThat(){ return true; }
SCENARIO( "Do that thing with the thing", "[tags]" ) { SCENARIO( "Do that thing with the thing", "[tags]" ) {
GIVEN( "This stuff exists" ) { GIVEN( "This stuff exists" ) {
@ -26,7 +29,11 @@ SCENARIO( "Do that thing with the thing", "[tags]" ) {
WHEN( "I do this" ) { WHEN( "I do this" ) {
// do this // do this
THEN( "it should do this") THEN( "it should do this")
{
REQUIRE( itDoesThis() ); REQUIRE( itDoesThis() );
AND_THEN( "do that")
REQUIRE( itDoesThat() );
}
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
* CATCH v0.9 build 20 (integration branch) * CATCH v0.9 build 21 (integration branch)
* Generated: 2013-02-19 19:57:51.967870 * Generated: 2013-03-04 12:17:59.865403
* ---------------------------------------------------------- * ----------------------------------------------------------
* 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.
@ -440,6 +440,8 @@ private:
#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED #define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
#include <sstream> #include <sstream>
#include <iomanip>
#include <limits>
#ifdef __OBJC__ #ifdef __OBJC__
// #included from: catch_objc_arc.hpp // #included from: catch_objc_arc.hpp
@ -494,37 +496,53 @@ namespace Detail {
template<typename T> NonStreamable( const T& ){} template<typename T> NonStreamable( const T& ){}
}; };
// If the type does not have its own << overload for ostream then } // end namespace Detail
// this one will be used instead
inline std::ostream& operator << ( std::ostream& ss, NonStreamable ){
return ss << "{?}";
}
template<typename T> // If the type does not have its own << overload for ostream then
inline std::string makeString( const T& value ) { // this one will be used instead
inline std::ostream& operator << ( std::ostream& ss, Detail::NonStreamable ){
return ss << "{?}";
}
template<typename T>
struct StringMaker {
static std::string convert( T const& value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << value;
return oss.str(); return oss.str();
} }
};
template<typename T> template<typename T>
inline std::string makeString( T* p ) { struct StringMaker<T*> {
static std::string convert( T const* p ) {
if( !p ) if( !p )
return INTERNAL_CATCH_STRINGIFY( NULL ); return INTERNAL_CATCH_STRINGIFY( NULL );
std::ostringstream oss; std::ostringstream oss;
oss << p; oss << p;
return oss.str(); return oss.str();
} }
};
template<typename T> template<typename T>
inline std::string makeString( const T* p ) { struct StringMaker<std::vector<T> > {
if( !p ) static std::string convert( std::vector<T> const& v ) {
return INTERNAL_CATCH_STRINGIFY( NULL );
std::ostringstream oss; std::ostringstream oss;
oss << p; oss << "{ ";
for( std::size_t i = 0; i < v.size(); ++ i ) {
oss << v[i];
if( i < v.size() - 1 )
oss << ", ";
}
oss << " }";
return oss.str(); return oss.str();
} }
};
namespace Detail {
template<typename T>
inline std::string makeString( const T& value ) {
return StringMaker<T>::convert( value );
}
} // end namespace Detail } // end namespace Detail
/// \brief converts any type to a string /// \brief converts any type to a string
@ -536,7 +554,8 @@ namespace Detail {
/// to provide an ostream overload for. /// to provide an ostream overload for.
template<typename T> template<typename T>
std::string toString( const T& value ) { std::string toString( const T& value ) {
return Detail::makeString( value ); return StringMaker<T>::convert( value );
// return Detail::makeString( value );
} }
// Built in overloads // Built in overloads
@ -583,7 +602,8 @@ inline std::string toString( unsigned int value ) {
inline std::string toString( const double value ) { inline std::string toString( const double value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << std::setprecision (std::numeric_limits<double>::digits10 + 1)
<< value;
return oss.str(); return oss.str();
} }
@ -4318,12 +4338,14 @@ namespace Catch {
return false; return false;
} }
void endSection( const std::string& ) { void endSection( const std::string&, bool stealth ) {
if( m_currentSection->ran() ) { if( m_currentSection->ran() ) {
if( !stealth )
m_runStatus = RanAtLeastOneSection; m_runStatus = RanAtLeastOneSection;
m_changed = true; m_changed = true;
} }
else if( m_runStatus == EncounteredASection ) { else if( m_runStatus == EncounteredASection ) {
if( !stealth )
m_runStatus = RanAtLeastOneSection; m_runStatus = RanAtLeastOneSection;
m_lastSectionToRun = m_currentSection; m_lastSectionToRun = m_currentSection;
} }
@ -4544,7 +4566,7 @@ namespace Catch {
missingAssertions = true; missingAssertions = true;
} }
m_runningTest->endSection( info.name ); m_runningTest->endSection( info.name, false );
m_reporter->sectionEnded( SectionStats( info, assertions, missingAssertions ) ); m_reporter->sectionEnded( SectionStats( info, assertions, missingAssertions ) );
m_messages.clear(); m_messages.clear();
@ -5352,6 +5374,10 @@ namespace Catch {
} // end namespace Catch } // end namespace Catch
#if !defined(CATCH_CONFIG_USE_ANSI_COLOUR_CODES) && !defined(CATCH_PLATFORM_WINDOWS)
#define CATCH_CONFIG_USE_ANSI_COLOUR_CODES 1
#endif
#if defined( CATCH_CONFIG_USE_ANSI_COLOUR_CODES ) #if defined( CATCH_CONFIG_USE_ANSI_COLOUR_CODES )
#include <unistd.h> #include <unistd.h>
@ -5373,8 +5399,15 @@ namespace Catch {
namespace { const char colourEscape = '\033'; } namespace { const char colourEscape = '\033'; }
inline bool shouldUseColour() {
static bool s_shouldUseColour
= CATCH_CONFIG_USE_ANSI_COLOUR_CODES != 0 &&
isatty( fileno(stdout) ) &&
!isDebuggerActive();
return s_shouldUseColour;
}
void TextColour::set( Colours colour ) { void TextColour::set( Colours colour ) {
if( isatty( fileno(stdout) ) && !isDebuggerActive() ) { if( shouldUseColour() ) {
switch( colour ) { switch( colour ) {
case TextColour::FileName: case TextColour::FileName:
std::cout << colourEscape << "[0m"; // white/ normal std::cout << colourEscape << "[0m"; // white/ normal
@ -5711,12 +5744,10 @@ namespace Catch {
else if( m_exprComponents.op == "matches" ) else if( m_exprComponents.op == "matches" )
return m_exprComponents.lhs + " " + m_exprComponents.rhs; return m_exprComponents.lhs + " " + m_exprComponents.rhs;
else if( m_exprComponents.op != "!" ) { else if( m_exprComponents.op != "!" ) {
if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 30 ) if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 )
return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
else if( m_exprComponents.lhs.size() < 70 && m_exprComponents.rhs.size() < 70 )
return "\n\t" + m_exprComponents.lhs + "\n\t" + m_exprComponents.op + "\n\t" + m_exprComponents.rhs;
else else
return "\n" + m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs + "\n\n"; return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
} }
else else
return "{can't expand - use " + info.macroName + "_FALSE( " + info.capturedExpression.substr(1) + " ) instead of " + info.macroName + "( " + info.capturedExpression + " ) for better diagnostics}"; return "{can't expand - use " + info.macroName + "_FALSE( " + info.capturedExpression.substr(1) + " ) instead of " + info.macroName + "( " + info.capturedExpression + " ) for better diagnostics}";
@ -5838,7 +5869,7 @@ namespace Catch {
namespace Catch { namespace Catch {
// These numbers are maintained by a script // These numbers are maintained by a script
Version libraryVersion( 0, 9, 20, "integration" ); Version libraryVersion( 0, 9, 21, "integration" );
} }
// #included from: catch_line_wrap.hpp // #included from: catch_line_wrap.hpp
@ -7083,13 +7114,18 @@ namespace Catch {
} }
void print() const { void print() const {
printSourceInfo();
if( stats.totals.assertions.total() > 0 ) { if( stats.totals.assertions.total() > 0 ) {
if( result.isOk() )
stream << "\n";
printResultType(); printResultType();
printOriginalExpression(); printOriginalExpression();
printReconstructedExpression(); printReconstructedExpression();
} }
else {
stream << "\n";
}
printMessage(); printMessage();
printSourceInfo();
} }
private: private:
@ -7131,7 +7167,7 @@ namespace Catch {
} }
void printSourceInfo() const { void printSourceInfo() const {
TextColour colourGuard( TextColour::FileName ); TextColour colourGuard( TextColour::FileName );
stream << result.getSourceInfo() << ":\n"; stream << result.getSourceInfo() << ": ";
} }
static std::string wrapLongStrings( std::string const& _string ){ static std::string wrapLongStrings( std::string const& _string ){