Use new line wrapper to show test case list, with tags, in columns

This commit is contained in:
Phil Nash 2013-03-28 22:13:31 +00:00
parent 9e8abc33e7
commit 15fd032608
8 changed files with 137 additions and 76 deletions

View File

@ -174,11 +174,11 @@
#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;

View File

@ -124,7 +124,7 @@ namespace Catch {
// Handle list request
if( config.listSpec != List::None ) {
List( config );
list( config );
Catch::cleanUp();
return 0;
}

View File

@ -15,7 +15,6 @@ namespace Catch {
class LineWrapper {
public:
LineWrapper( std::size_t _right );
LineWrapper();
LineWrapper& setIndent( std::size_t _indent );
@ -29,17 +28,22 @@ namespace Catch {
const_iterator begin() const { return lines.begin(); }
const_iterator end() const { return lines.end(); }
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<std::string> lines;
};

View File

@ -12,15 +12,12 @@
namespace Catch {
LineWrapper::LineWrapper( std::size_t _right )
: right( _right ),
nextTab( 0 ),
tab( 0 )
{}
LineWrapper::LineWrapper()
: right( CATCH_CONFIG_CONSOLE_WIDTH-1 ),
nextTab( 0 ),
tab( 0 )
tab( 0 ),
wrappableChars( " [({.," ),
recursionCount( 0 )
{}
LineWrapper& LineWrapper::setIndent( std::size_t _indent ) {
@ -36,7 +33,12 @@ namespace Catch {
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 ) {
@ -51,6 +53,9 @@ namespace Catch {
addLine( _str.substr( 0, wrapPoint ) );
while( _str[++wrapPoint] == ' ' );
}
else if( isWrapPoint( _str[wrapPoint] ) ) {
addLine( _str.substr( 0, wrapPoint ) );
}
else {
addLine( _str.substr( 0, --wrapPoint ) + '-' );
}
@ -61,7 +66,7 @@ namespace Catch {
std::string withoutTab = _str.substr( 0, nextTab ) + _str.substr( nextTab+1 );
return wrapInternal( withoutTab );
}
else if( _str[pos] == ' ' ) {
else if( isWrapPoint( _str[pos] ) ) {
wrapPoint = pos;
}
}

View File

@ -9,7 +9,10 @@
#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
#include "catch_commandline.hpp"
#include "catch_line_wrap.h"
#include <limits>
#include <algorithm>
namespace Catch {
inline bool matchesFilters( const std::vector<TestCaseFilters>& filters, const TestCase& testCase ) {
@ -20,47 +23,80 @@ 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";
}
std::cout << std::endl;
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<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
std::vector<TestCase>::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 ) {
maxTagLen = (std::max)( it->getTestCaseInfo().tagsAsString.size(), maxTagLen );
maxNameLen = (std::max)( it->getTestCaseInfo().name.size(), maxNameLen );
}
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<TestCase>::const_iterator it = getRegistryHub().getTestCaseRegistry().getAllTests().begin();
std::vector<TestCase>::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 << " " << std::string( maxNameLen - nameCol.size(), ' ' );
if( i < tagsWrapper.size() )
std::cout << 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.listSpec & List::All ) == 0 ) {
std::ostringstream oss;
oss << "Unknown list type";
throw std::domain_error( oss.str() );
if( config.filters.empty() )
std::cout << pluralise( matchedTests, "test case" ) << std::endl;
else
std::cout << pluralise( matchedTests, "matching test case" ) << 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 );
else if( config.listSpec & List::Reports )
listReporters( config );
else
throw std::logic_error( "Unknown list type" );
}
} // end namespace Catch

View File

@ -36,6 +36,7 @@ namespace Catch {
std::string className;
std::string description;
std::set<std::string> tags;
std::string tagsAsString;
SourceLineInfo lineInfo;
bool isHidden;
};

View File

@ -44,15 +44,21 @@ namespace Catch {
tags( _tags ),
lineInfo( _lineInfo ),
isHidden( _isHidden )
{}
{
std::ostringstream oss;
for( std::set<std::string>::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 )
isHidden( other.isHidden )
{}
TestCase::TestCase( ITestCase* testCase, const TestCaseInfo& info ) : TestCaseInfo( info ), test( testCase ) {}

View File

@ -432,31 +432,40 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) {
std::string testString = "one two three four";
SECTION( "No wrapping", "" ) {
CHECK( Catch::LineWrapper( 80 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper( 18 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper().setRight( 80 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper().setRight( 18 ).wrap( testString ).toString() == testString );
}
SECTION( "Wrapped once", "" ) {
CHECK( Catch::LineWrapper( 17 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper( 16 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper( 15 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper( 14 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper( 13 ).wrap( testString ).toString() == "one two\nthree four" );
CHECK( Catch::LineWrapper().setRight( 17 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper().setRight( 16 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper().setRight( 15 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper().setRight( 14 ).wrap( testString ).toString() == "one two three\nfour" );
CHECK( Catch::LineWrapper().setRight( 13 ).wrap( testString ).toString() == "one two\nthree four" );
}
SECTION( "Wrapped twice", "" ) {
CHECK( Catch::LineWrapper( 9 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper( 8 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 9 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 8 ).wrap( testString ).toString() == "one two\nthree\nfour" );
}
SECTION( "Wrapped three times", "" ) {
CHECK( Catch::LineWrapper( 7 ).wrap( testString ).toString() == "one\ntwo\nthree\nfour" );
CHECK( Catch::LineWrapper( 5 ).wrap( testString ).toString() == "one\ntwo\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 7 ).wrap( testString ).toString() == "one\ntwo\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 5 ).wrap( testString ).toString() == "one\ntwo\nthree\nfour" );
}
SECTION( "Short wrap", "" ) {
CHECK( Catch::LineWrapper( 4 ).wrap( "abcdef" ).toString() == "abc-\ndef" );
CHECK( Catch::LineWrapper( 4 ).wrap( "abcdefg" ).toString() == "abc-\ndefg" );
CHECK( Catch::LineWrapper( 4 ).wrap("abcdefgh" ).toString() == "abc-\ndef-\ngh" );
CHECK( Catch::LineWrapper().setRight( 4 ).wrap( "abcdef" ).toString() == "abc-\ndef" );
CHECK( Catch::LineWrapper().setRight( 4 ).wrap( "abcdefg" ).toString() == "abc-\ndefg" );
CHECK( Catch::LineWrapper().setRight( 4 ).wrap("abcdefgh" ).toString() == "abc-\ndef-\ngh" );
CHECK( Catch::LineWrapper( 4 ).wrap( testString ).toString() == "one\ntwo\nthr-\nee\nfour" );
CHECK( Catch::LineWrapper( 3 ).wrap( testString ).toString() == "one\ntwo\nth-\nree\nfo-\nur" );
CHECK( Catch::LineWrapper().setRight( 4 ).wrap( testString ).toString() == "one\ntwo\nthr-\nee\nfour" );
CHECK( Catch::LineWrapper().setRight( 3 ).wrap( testString ).toString() == "one\ntwo\nth-\nree\nfo-\nur" );
}
SECTION( "As container", "" ) {
Catch::LineWrapper wrapper;
wrapper.setRight( 7 ).wrap( testString );
CHECK( wrapper.size() == 4 );
CHECK( wrapper[0] == "one" );
CHECK( wrapper[1] == "two" );
CHECK( wrapper[2] == "three" );
CHECK( wrapper[3] == "four" );
}
}
@ -466,22 +475,22 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) {
std::string testString = "one two\nthree four";
SECTION( "No wrapping" , "" ) {
CHECK( Catch::LineWrapper( 80 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper( 18 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper( 10 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper().setRight( 80 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper().setRight( 18 ).wrap( testString ).toString() == testString );
CHECK( Catch::LineWrapper().setRight( 10 ).wrap( testString ).toString() == testString );
}
SECTION( "Trailing newline" , "" ) {
CHECK( Catch::LineWrapper( 10 ).wrap( "abcdef\n" ).toString() == "abcdef\n" );
CHECK( Catch::LineWrapper( 6 ).wrap( "abcdef" ).toString() == "abcdef" );
CHECK( Catch::LineWrapper( 6 ).wrap( "abcdef\n" ).toString() == "abcdef\n" );
CHECK( Catch::LineWrapper().setRight( 10 ).wrap( "abcdef\n" ).toString() == "abcdef\n" );
CHECK( Catch::LineWrapper().setRight( 6 ).wrap( "abcdef" ).toString() == "abcdef" );
CHECK( Catch::LineWrapper().setRight( 6 ).wrap( "abcdef\n" ).toString() == "abcdef\n" );
}
SECTION( "Wrapped once", "" ) {
CHECK( Catch::LineWrapper( 9 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper( 8 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper( 7 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 9 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 8 ).wrap( testString ).toString() == "one two\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 7 ).wrap( testString ).toString() == "one two\nthree\nfour" );
}
SECTION( "Wrapped twice", "" ) {
CHECK( Catch::LineWrapper( 6 ).wrap( testString ).toString() == "one\ntwo\nthree\nfour" );
CHECK( Catch::LineWrapper().setRight( 6 ).wrap( testString ).toString() == "one\ntwo\nthree\nfour" );
}
}