From 38f7eef6feeaf65b7b65e49da74ffc2c5cc19398 Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Fri, 29 Mar 2013 13:44:33 +0000 Subject: [PATCH] build 29 --- README.md | 2 +- include/internal/catch_version.hpp | 2 +- single_include/catch.hpp | 388 ++++++++++++++++++++--------- 3 files changed, 275 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 329c4592..71360025 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![catch logo](https://raw.github.com/philsquared/Catch/Integration/catch-logo-small.png) -## CATCH v0.9 build 28 (integration branch) +## CATCH v0.9 build 29 (integration branch) An automated test framework for C, C++ and Objective-C. This branch may contain code that is experimental or not yet fully tested. diff --git a/include/internal/catch_version.hpp b/include/internal/catch_version.hpp index 9e61b30e..d271e4c7 100644 --- a/include/internal/catch_version.hpp +++ b/include/internal/catch_version.hpp @@ -13,7 +13,7 @@ namespace Catch { // These numbers are maintained by a script - Version libraryVersion( 0, 9, 28, "integration" ); + Version libraryVersion( 0, 9, 29, "integration" ); } #endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED diff --git a/single_include/catch.hpp b/single_include/catch.hpp index 3027eba8..c78dc5d2 100644 --- a/single_include/catch.hpp +++ b/single_include/catch.hpp @@ -1,6 +1,6 @@ /* - * CATCH v0.9 build 28 (integration branch) - * Generated: 2013-03-25 09:25:14.678493 + * CATCH v0.9 build 29 (integration branch) + * Generated: 2013-03-29 13:43:27.058903 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -1417,6 +1417,7 @@ namespace Catch { std::string className; std::string description; std::set tags; + std::string tagsAsString; SourceLineInfo lineInfo; bool isHidden; }; @@ -1917,9 +1918,8 @@ namespace Catch { Reports = 1, Tests = 2, - All = 3, - - TestNames = 6, + Tags = 4, + All = Reports | Tests | Tags, WhatMask = 0xf, @@ -3786,7 +3786,8 @@ namespace Catch { m_optionNames.push_back( "--list" ); } virtual std::string argsSynopsis() const { - return "[all | tests | reporters [xml]]"; +// return "[all | tests | reporters | tags [xml]]"; + return "[all | tests | reporters | tags]"; } virtual std::string optionSummary() const { return "Lists available tests or reporters"; @@ -3795,29 +3796,32 @@ namespace Catch { virtual std::string optionDescription() const { return "With no arguments this option will list all registered tests - one per line.\n" - "Supplying the xml argument formats the list as an xml document (which may be useful for " - "consumption by other tools).\n" +// "Supplying the xml argument formats the list as an xml document (which may be useful for " +// "consumption by other tools).\n" "Supplying the tests or reporters lists tests or reporters respectively - with descriptions.\n" "\n" "Examples:\n" "\n" " -l\n" " -l tests\n" + " -l tags\n" " -l reporters xml\n" - " -l xml"; + ;//" -l xml"; } virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { - config.listSpec = List::TestNames; + config.listSpec = List::Tests; if( cmd.argsCount() >= 1 ) { if( cmd[0] == "all" ) config.listSpec = List::All; else if( cmd[0] == "tests" ) config.listSpec = List::Tests; + else if( cmd[0] == "tags" ) + config.listSpec = List::Tags; else if( cmd[0] == "reporters" ) config.listSpec = List::Reports; else - cmd.raiseError( "Expected [tests] or [reporters]" ); + cmd.raiseError( "Expected tests, reporters or tags" ); } if( cmd.argsCount() >= 2 ) { if( cmd[1] == "xml" ) @@ -3825,7 +3829,7 @@ namespace Catch { else if( cmd[1] == "text" ) config.listSpec = static_cast( config.listSpec | List::AsText ); else - cmd.raiseError( "Expected [xml] or [text]" ); + cmd.raiseError( "Expected xml or text" ); } } }; @@ -4144,7 +4148,53 @@ namespace Catch { // #included from: internal/catch_list.hpp #define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED +// #included from: catch_line_wrap.h +#define TWOBLUECUBES_CATCH_LINE_WRAP_H_INCLUDED + +#include +#include + +namespace Catch { + + class LineWrapper { + public: + LineWrapper(); + + LineWrapper& setIndent( std::size_t _indent ); + LineWrapper& setRight( std::size_t _right ); + + LineWrapper& wrap( std::string const& _str ); + + std::string toString() const; + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + + friend std::ostream& operator << ( std::ostream& _stream, LineWrapper const& _lineWrapper ); + + private: + void wrapInternal( std::string const& _str ); + void addLine( const std::string& _line ); + bool isWrapPoint( char c ); + + std::string indent; + std::size_t right; + std::size_t nextTab; + std::size_t tab; + std::string wrappableChars; + int recursionCount; + std::vector lines; + }; + +} // end namespace Catch + #include +#include namespace Catch { inline bool matchesFilters( const std::vector& filters, const TestCase& testCase ) { @@ -4155,47 +4205,136 @@ namespace Catch { return false; return true; } - inline void List( const ConfigData& config ) { - if( config.listSpec & List::Reports ) { - std::cout << "Available reports:\n"; - IReporterRegistry::FactoryMap::const_iterator it = getRegistryHub().getReporterRegistry().getFactories().begin(); - IReporterRegistry::FactoryMap::const_iterator itEnd = getRegistryHub().getReporterRegistry().getFactories().end(); - for(; it != itEnd; ++it ) { - // !TBD: consider listAs() - std::cout << "\t" << it->first << "\n\t\t'" << it->second->getDescription() << "'\n"; + inline void listTests( const ConfigData& config ) { + if( config.filters.empty() ) + std::cout << "All available test cases:\n"; + else + std::cout << "Matching test cases:\n"; + std::vector const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); + std::vector::const_iterator it = allTests.begin(), itEnd = allTests.end(); + + // First pass - get max tags + std::size_t maxTagLen = 0; + std::size_t maxNameLen = 0; + for(; it != itEnd; ++it ) { + if( matchesFilters( config.filters, *it ) ) { + maxTagLen = (std::max)( it->getTestCaseInfo().tagsAsString.size(), maxTagLen ); + maxNameLen = (std::max)( it->getTestCaseInfo().name.size(), maxNameLen ); } - std::cout << std::endl; } - if( config.listSpec & List::Tests ) { - if( config.filters.empty() ) - std::cout << "All available test cases:\n"; + // Try to fit everything in. If not shrink tag column first, down to 30 + // then shrink name column until it all fits (strings will be wrapped within column) + while( maxTagLen + maxNameLen > CATCH_CONFIG_CONSOLE_WIDTH-5 ) { + if( maxTagLen > 30 ) + --maxTagLen; else - std::cout << "Matching test cases:\n"; - std::vector::const_iterator it = getRegistryHub().getTestCaseRegistry().getAllTests().begin(); - std::vector::const_iterator itEnd = getRegistryHub().getTestCaseRegistry().getAllTests().end(); - std::size_t matchedTests = 0; - for(; it != itEnd; ++it ) { - if( matchesFilters( config.filters, *it ) ) { - matchedTests++; - // !TBD: consider listAs() - std::cout << " " << it->getTestCaseInfo().name << "\n"; - if( ( config.listSpec & List::TestNames ) != List::TestNames ) - std::cout << " '" << it->getTestCaseInfo().description << "'\n"; + --maxNameLen; + } + + std::size_t matchedTests = 0; + for( it = allTests.begin(); it != itEnd; ++it ) { + if( matchesFilters( config.filters, *it ) ) { + matchedTests++; + // !TBD: consider listAs() + LineWrapper nameWrapper; + nameWrapper.setRight( maxNameLen ).setIndent( 2 ).wrap( it->getTestCaseInfo().name ); + + LineWrapper tagsWrapper; + tagsWrapper.setRight( maxTagLen ).wrap( it->getTestCaseInfo().tagsAsString ); + + for( std::size_t i = 0; i < std::max( nameWrapper.size(), tagsWrapper.size() ); ++i ) { + std::string nameCol; + if( i < nameWrapper.size() ) + nameCol = nameWrapper[i]; + else + nameCol = " ..."; + std::cout << nameCol; + if( i < tagsWrapper.size() && !tagsWrapper[i].empty() ) { + if( i == 0 ) + std::cout << " " << std::string( maxNameLen - nameCol.size(), '.' ) << " " << tagsWrapper[i]; + else + std::cout << std::string( maxNameLen - nameCol.size(), ' ' ) << " " << tagsWrapper[i]; + } + std::cout << "\n"; } } - if( config.filters.empty() ) - std::cout << pluralise( matchedTests, "test case" ) << std::endl; - else - std::cout << pluralise( matchedTests, "matching test case" ) << std::endl; } + if( config.filters.empty() ) + std::cout << pluralise( matchedTests, "test case" ) << std::endl; + else + std::cout << pluralise( matchedTests, "matching test case" ) << std::endl; + } - if( ( config.listSpec & List::All ) == 0 ) { - std::ostringstream oss; - oss << "Unknown list type"; - throw std::domain_error( oss.str() ); + inline void listTags( const ConfigData& config ) { + if( config.filters.empty() ) + std::cout << "All available tags:\n"; + else + std::cout << "Matching tags:\n"; + std::vector const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); + std::vector::const_iterator it = allTests.begin(), itEnd = allTests.end(); + + std::map tagCounts; + + std::size_t maxTagLen = 0; + + for(; it != itEnd; ++it ) { + if( matchesFilters( config.filters, *it ) ) { + for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = "[" + *tagIt + "]"; + maxTagLen = (std::max)( maxTagLen, tagName.size() ); + std::map::iterator countIt = tagCounts.find( tagName ); + if( countIt == tagCounts.end() ) + tagCounts.insert( std::make_pair( tagName, 1 ) ); + else + countIt->second++; + } + } } + maxTagLen +=2; + if( maxTagLen > CATCH_CONFIG_CONSOLE_WIDTH-10 ) + maxTagLen = CATCH_CONFIG_CONSOLE_WIDTH-10; + + for( std::map::const_iterator countIt = tagCounts.begin(), countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + LineWrapper wrapper; + wrapper.setIndent(2).setRight( maxTagLen ).wrap( countIt->first ); + + std::cout << wrapper; + if( maxTagLen > wrapper.last().size() ) + std::cout << std::string( maxTagLen - wrapper.last().size(), '.' ); + std::cout << ".. " + << countIt->second + << "\n"; + } + std::cout << pluralise( tagCounts.size(), "tag" ) << std::endl; + } + + inline void listReporters( const ConfigData& /*config*/ ) { + std::cout << "Available reports:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator it = factories.begin(), itEnd = factories.end(); + for(; it != itEnd; ++it ) { + // !TBD: consider listAs() + std::cout << "\t" << it->first << "\n\t\t'" << it->second->getDescription() << "'\n"; + } + std::cout << std::endl; + } + + inline void list( const ConfigData& config ) { + if( config.listSpec & List::Tests ) + listTests( config ); + if( config.listSpec & List::Tags ) + listTags( config ); + if( config.listSpec & List::Reports ) + listReporters( config ); + if( ( config.listSpec & List::All ) == 0 ) + throw std::logic_error( "Unknown list type" ); } } // end namespace Catch @@ -4748,18 +4887,6 @@ namespace Catch { extern Version libraryVersion; } -// #included from: internal/catch_line_wrap.h -#define TWOBLUECUBES_CATCH_LINE_WRAP_H_INCLUDED - -#include - -namespace Catch { - - void wrapLongStrings( std::ostream& stream, const std::string& str, std::size_t columns, std::size_t indent = 0 ); - std::string wrapLongStrings( const std::string& str, std::size_t columns, std::size_t indent = 0 ); - -} // end namespace Catch - #include #include #include @@ -4869,7 +4996,7 @@ namespace Catch { // Handle list request if( config.listSpec != List::None ) { - List( config ); + list( config ); Catch::cleanUp(); return 0; } @@ -4906,7 +5033,7 @@ namespace Catch { displayedSpecificOption = true; std::cout << "\n" << opt.optionNames() << " " << opt.argsSynopsis() << "\n\n" << opt.optionSummary() << "\n\n" - << wrapLongStrings( opt.optionDescription(), 80, 2 ) << "\n" << std::endl; + << LineWrapper().setIndent( 2 ).wrap( opt.optionDescription() ) << "\n" << std::endl; } } @@ -5839,13 +5966,19 @@ namespace Catch { tags( _tags ), lineInfo( _lineInfo ), isHidden( _isHidden ) - {} + { + std::ostringstream oss; + for( std::set::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) + oss << "[" << *it << "]"; + tagsAsString = oss.str(); + } TestCaseInfo::TestCaseInfo( const TestCaseInfo& other ) : name( other.name ), className( other.className ), description( other.description ), tags( other.tags ), + tagsAsString( other.tagsAsString ), lineInfo( other.lineInfo ), isHidden( other.isHidden ) {} @@ -5919,7 +6052,7 @@ namespace Catch { namespace Catch { // These numbers are maintained by a script - Version libraryVersion( 0, 9, 28, "integration" ); + Version libraryVersion( 0, 9, 29, "integration" ); } // #included from: catch_line_wrap.hpp @@ -5927,62 +6060,91 @@ namespace Catch { namespace Catch { - namespace { - inline void addIndent( std::ostream& os, std::size_t indent ) { - while( indent-- > 0 ) - os << ' '; - } + LineWrapper::LineWrapper() + : right( CATCH_CONFIG_CONSOLE_WIDTH-1 ), + nextTab( 0 ), + tab( 0 ), + wrappableChars( " [({.," ), + recursionCount( 0 ) + {} - inline void recursivelyWrapLine( std::ostream& os, std::string paragraph, std::size_t columns, std::size_t indent ) { - std::size_t width = columns-indent; - std::size_t tab = 0; - std::size_t wrapPoint = width; - for( std::size_t pos = 0; pos < paragraph.size(); ++pos ) { - if( pos == width ) { - addIndent( os, indent ); - if( paragraph[wrapPoint] == ' ' ) { - os << paragraph.substr( 0, wrapPoint ) << "\n"; - while( paragraph[++wrapPoint] == ' ' ); - } - else { - os << paragraph.substr( 0, --wrapPoint ) << "-\n"; - } - return recursivelyWrapLine( os, paragraph.substr( wrapPoint ), columns, indent+tab ); - } - if( paragraph[pos] == '\t' ) { - tab = pos; - paragraph = paragraph.substr( 0, tab ) + paragraph.substr( tab+1 ); - pos--; - } - else if( paragraph[pos] == ' ' ) { - wrapPoint = pos; - } + LineWrapper& LineWrapper::setIndent( std::size_t _indent ) { + indent = std::string( _indent, ' ' ); + return *this; + } + LineWrapper& LineWrapper::setRight( std::size_t _right ) { + right = _right; + return *this; + } + LineWrapper& LineWrapper::wrap( std::string const& _str ) { + nextTab = tab = 0; + wrapInternal( _str ); + return *this; + } + bool LineWrapper::isWrapPoint( char c ) { + return wrappableChars.find( c ) != std::string::npos; + } + void LineWrapper::wrapInternal( std::string const& _str ) { + assert( ++recursionCount < 100 ); + + std::size_t width = right - indent.size(); + std::size_t wrapPoint = width-tab; + for( std::size_t pos = 0; pos < _str.size(); ++pos ) { + if( _str[pos] == '\n' ) + { + addLine( _str.substr( 0, pos ) ); + nextTab = tab = 0; + return wrapInternal( _str.substr( pos+1 ) ); + } + if( pos == width-tab ) { + if( _str[wrapPoint] == ' ' ) { + addLine( _str.substr( 0, wrapPoint ) ); + while( _str[++wrapPoint] == ' ' ); + } + else if( isWrapPoint( _str[wrapPoint] ) ) { + addLine( _str.substr( 0, wrapPoint ) ); + } + else { + addLine( _str.substr( 0, --wrapPoint ) + '-' ); + } + return wrapInternal( _str.substr( wrapPoint ) ); + } + if( _str[pos] == '\t' ) { + nextTab = pos; + std::string withoutTab = _str.substr( 0, nextTab ) + _str.substr( nextTab+1 ); + return wrapInternal( withoutTab ); + } + else if( pos > 0 && isWrapPoint( _str[pos] ) ) { + wrapPoint = pos; } - addIndent( os, indent ); - os << paragraph; } + addLine( _str ); } - void wrapLongStrings( std::ostream& stream, const std::string& str, std::size_t columns, std::size_t indent ) { - std::string::size_type pos = 0; - std::string::size_type newline = str.find_first_of( '\n' ); - while( newline != std::string::npos ) { - std::string paragraph = str.substr( pos, newline-pos ); - recursivelyWrapLine( stream, paragraph, columns, indent ); - stream << "\n"; - pos = newline+1; - newline = str.find_first_of( '\n', pos ); + std::ostream& operator << ( std::ostream& _stream, LineWrapper const& _lineWrapper ) { + for( LineWrapper::const_iterator it = _lineWrapper.begin(), itEnd = _lineWrapper.end(); + it != itEnd; ++it ) { + if( it != _lineWrapper.begin() ) + _stream << "\n"; + _stream << *it; } - if( pos != str.size() ) - recursivelyWrapLine( stream, str.substr( pos, str.size()-pos ), columns, indent ); + return _stream; } - - std::string wrapLongStrings( const std::string& str, std::size_t columns, std::size_t indent ) { + std::string LineWrapper::toString() const { std::ostringstream oss; - wrapLongStrings( oss, str, columns, indent ); + oss << *this; return oss.str(); } + void LineWrapper::addLine( const std::string& _line ) { + if( tab > 0 ) + lines.push_back( indent + std::string( tab, ' ' ) + _line ); + else + lines.push_back( indent + _line ); + if( nextTab > 0 ) + tab = nextTab; + } + } // end namespace Catch // #included from: catch_message.hpp @@ -7207,7 +7369,7 @@ namespace Catch { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; TextColour colourGuard( TextColour::ReconstructedExpression ); - stream << wrapLongStrings( result.getExpandedExpression() ) << "\n"; + stream << LineWrapper().setIndent(2).wrap( result.getExpandedExpression() ) << "\n"; } } void printMessage() const { @@ -7216,7 +7378,7 @@ namespace Catch { for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { - stream << wrapLongStrings( it->message ) << "\n"; + stream << LineWrapper().setIndent(2).wrap( it->message ) << "\n"; } } void printSourceInfo() const { @@ -7224,10 +7386,6 @@ namespace Catch { stream << result.getSourceInfo() << ": "; } - static std::string wrapLongStrings( std::string const& _string ){ - return Catch::wrapLongStrings( _string, CATCH_CONFIG_CONSOLE_WIDTH-1, 2 ); - } - std::ostream& stream; AssertionStats const& stats; AssertionResult const& result; @@ -7599,11 +7757,11 @@ int main (int argc, char * const argv[]) { #else #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) #endif -#define GIVEN( desc ) SECTION( "Given: " desc, "" ) -#define WHEN( desc ) SECTION( " When: " desc, "" ) -#define AND_WHEN( desc ) SECTION( " And: " desc, "" ) -#define THEN( desc ) SECTION( " Then: " desc, "" ) -#define AND_THEN( desc ) SECTION( " And: " desc, "" ) +#define GIVEN( desc ) SECTION( " Given: " desc, "" ) +#define WHEN( desc ) SECTION( " When: " desc, "" ) +#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) +#define THEN( desc ) SECTION( " Then: " desc, "" ) +#define AND_THEN( desc ) SECTION( " And: " desc, "" ) using Catch::Detail::Approx;