Merge from origin

This commit is contained in:
Malcolm Noyes 2013-11-14 20:12:14 +00:00
commit 3a6ab65c82
9 changed files with 245 additions and 196 deletions

View File

@ -1,6 +1,6 @@
![catch logo](catch-logo-small.png) ![catch logo](catch-logo-small.png)
*v1.0 build 11 (master branch)* *v1.0 build 13 (master branch)*
Build status (on Travis CI) [![Build Status](https://travis-ci.org/philsquared/Catch.png)](https://travis-ci.org/philsquared/Catch) Build status (on Travis CI) [![Build Status](https://travis-ci.org/philsquared/Catch.png)](https://travis-ci.org/philsquared/Catch)

View File

@ -21,7 +21,7 @@ Note that options are described according to the following pattern:
Test cases, wildcarded test cases, tags and tag expressions are all passed directly as arguments. Tags are distinguished by being enclosed in square brackets. Test cases, wildcarded test cases, tags and tag expressions are all passed directly as arguments. Tags are distinguished by being enclosed in square brackets.
If no test specs are supplied then all test cases, except "hidden" tests (tagged ```[hide]``` or, in the legacy case, prefixed by `'./'`) are run If no test specs are supplied then all test cases, except "hidden" tests (tagged ```[hide]```, ```[.]``` or, in the legacy case, prefixed by `'./'`) are run.
Specs must be enclosed in quotes if they contain spaces. If they do not contain spaces the quotes are optional. Specs must be enclosed in quotes if they contain spaces. If they do not contain spaces the quotes are optional.

View File

@ -15,7 +15,22 @@ For examples see the [Tutorial](tutorial.md)
## Tags ## Tags
-{placeholder for documentation of tags}- Tags allow an arbitrary number of additional strings to be associated with a test case. Test cases can be selected (for running, or just for listing) by tag - or even by an expression that combines several tags. At their most basic level they provide a simple way to group several related tests together.
As an example - given the following test cases:
TEST_CASE( "A", "[widget]" ) { /* ... */ }
TEST_CASE( "B", "[widget]" ) { /* ... */ }
TEST_CASE( "C", "[gadget]" ) { /* ... */ }
TEST_CASE( "D", "[widget][gadget]" ) { /* ... */ }
The tag expression, ```"[widget]"``` selects A, B & D. ```"[gadget]"``` selects C & D. ```"[widget][gadget]"``` selects just D and ```"[widget],[gadget]"``` selects all four test cases.
For more detail on command line selection see [the command line docs](command-line.md#specifying-which-tests-to-run)
A special tag name, ```[hide]``` causes test cases to be skipped from the default list (ie when no test cases have been explicitly selected through tag expressions or name wildcards). ```[.]``` is an alias for ```[hide]```.
Tag names are not case sensitive.
## BDD-style test cases ## BDD-style test cases

View File

@ -87,11 +87,11 @@ Of course there are still more issues to do deal with. For example we'll hit pro
Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take moment to consider those before we move on. Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take moment to consider those before we move on.
1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md). 1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md).
2. We introduce test cases with the TEST_CASE macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#testCasesAndSections">Test cases and Sections</a>, below. The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md) for more information on running tests. 2. We introduce test cases with the TEST_CASE macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#test-cases-and-sections">Test cases and Sections</a>, below. The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md) for more information on running tests.
3. The name and tags arguments are just strings. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you, and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names. 3. The name and tags arguments are just strings. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you, and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names.
4. We write our individual test assertions using the REQUIRE macro. Rather than a separate macro for each type of condition we express the condition naturally using C/C++ syntax. Behind the scenes a simple set of expression templates captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. As we'll see later there _are_ other assertion macros - but because of this technique the number of them is drastically reduced. 4. We write our individual test assertions using the REQUIRE macro. Rather than a separate macro for each type of condition we express the condition naturally using C/C++ syntax. Behind the scenes a simple set of expression templates captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. As we'll see later there _are_ other assertion macros - but because of this technique the number of them is drastically reduced.
<a id="testCasesAndSections"></a> <a id="test-cases-and-sections"></a>
## Test cases and sections ## Test cases and sections
Most test frameworks have a class-based fixture mechanism. That is, test cases map to methods on a class and common setup and teardown can be performed in ```setup()``` and ```teardown()``` methods (or constructor/ destructor in languages, like C++, that support deterministic destruction). Most test frameworks have a class-based fixture mechanism. That is, test cases map to methods on a class and common setup and teardown can be performed in ```setup()``` and ```teardown()``` methods (or constructor/ destructor in languages, like C++, that support deterministic destruction).
@ -140,7 +140,82 @@ This works because the ```SECTION``` macro contains an if statement that calls b
So far so good - this is already an improvement on the setup/ teardown approach because now we see our setup code inline and we can use the stack. So far so good - this is already an improvement on the setup/ teardown approach because now we see our setup code inline and we can use the stack.
-{placeholder for documentation on nested sections}- The power of sections really shows, however, when we need to execute a sequence of, checked, operations. Continuing the vector example we might want to verify that after reserving a larger capacity, if we reserve smaller capacity (but still larger than the current size) then the capacity is not, in fact, changed. We can do that, naturally, like so:
SECTION( "reserving bigger changes capacity but not size" ) {
v.reserve( 10 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 10 );
SECTION( "reserving smaller again does not change capacity" ) {
v.reserve( 7 );
REQUIRE( v.capacity() >= 10 );
}
}
Sections can be nested to an arbitrary depth (limited only by your stack size). Each leaf section (i.e. a section that contains no nested sections) will be executed exactly once, on a separate path of execution from any other leaf section (so no leaf section can interfere with another). Obviously a failure in a parent section will prevent nested sections from running - but that's the idea.
## BDD-Style
If you name your test cases and sections appropriately you can achieve a BDD-style specification structure. This became such a useful way of working that first class support has been added to Catch. Scenarios can be specified using ```SCENARIO```, ```GIVEN```, ```WHEN``` and ```THEN``` macros, which map on to ```TEST_CASE```s and ```SECTION```s, respectively (for more details see [Test cases and sections](test-cases-and-sections.md)).
The vector example can be adjusted to use these macros like so:
```c++
SCENARIO( "vectors can be sized and resized", "[vector]" ) {
GIVEN( "A vector with some items" ) {
std::vector<int> v( 5 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
WHEN( "the size is increased" ) {
v.resize( 10 );
THEN( "the size and capacity change" ) {
REQUIRE( v.size() == 10 );
REQUIRE( v.capacity() >= 10 );
}
}
WHEN( "the size is reduced" ) {
v.resize( 0 );
THEN( "the size changes but not capacity" ) {
REQUIRE( v.size() == 0 );
REQUIRE( v.capacity() >= 5 );
}
}
WHEN( "more capacity is reserved" ) {
v.reserve( 10 );
THEN( "the capacity changes but not the size" ) {
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 10 );
}
}
WHEN( "less capacity is reserved" ) {
v.reserve( 0 );
THEN( "neither size nor capacity are changed" ) {
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
}
}
}
}
```
A nice consequence of this is that when these tests are run the test names are reported like this:
```
Scenario: vectors can be sized and resized
Given: A vector with some items
When: more capacity is reserved
Then: the capacity changes but not the size
```
## Next steps ## Next steps
For more specific information see the [Reference pages](reference-index.md) For more specific information see the [Reference pages](reference-index.md)

View File

@ -30,76 +30,29 @@ namespace Catch {
std::cout << "All available test cases:\n"; std::cout << "All available test cases:\n";
else else
std::cout << "Matching test cases:\n"; 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 ) {
if( matchesFilters( config.filters(), *it ) ) {
maxTagLen = (std::max)( it->getTestCaseInfo().tagsAsString.size(), maxTagLen );
maxNameLen = (std::max)( it->getTestCaseInfo().name.size(), maxNameLen );
}
}
// 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
--maxNameLen;
}
std::size_t matchedTests = 0; std::size_t matchedTests = 0;
for( it = allTests.begin(); it != itEnd; ++it ) { TextAttributes nameAttr, tagsAttr;
nameAttr.setInitialIndent( 2 ).setIndent( 4 );
tagsAttr.setIndent( 6 );
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end();
it != itEnd;
++it )
if( matchesFilters( config.filters(), *it ) ) { if( matchesFilters( config.filters(), *it ) ) {
matchedTests++; matchedTests++;
Text nameWrapper( it->getTestCaseInfo().name, TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
TextAttributes() Colour::Code colour = testCaseInfo.isHidden
.setWidth( maxNameLen+2 ) ? Colour::SecondaryText
.setInitialIndent(2) : Colour::None;
.setIndent(4) ); Colour colourGuard( colour );
Text tagsWrapper( it->getTestCaseInfo().tagsAsString, std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl;
TextAttributes() if( !testCaseInfo.tags.empty() )
.setWidth( maxTagLen ) std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
.setInitialIndent(0)
.setIndent( 2 ) );
for( std::size_t i = 0; i < (std::max)( nameWrapper.size(), tagsWrapper.size() ); ++i ) {
Colour::Code colour = Colour::None;
if( it->getTestCaseInfo().isHidden )
colour = Colour::SecondaryText;
std::string nameCol;
if( i < nameWrapper.size() ) {
nameCol = nameWrapper[i];
}
else {
nameCol = " ...";
colour = Colour::SecondaryText;
}
{
Colour colourGuard( colour );
std::cout << nameCol;
}
if( i < tagsWrapper.size() && !tagsWrapper[i].empty() ) {
size_t padLen( maxNameLen > nameCol.size() ? maxNameLen - nameCol.size() : 0 );
if( i == 0 ) {
Colour colourGuard( Colour::SecondaryText );
std::cout << " " << std::string( padLen, '.' ) << " ";
}
else {
std::cout << std::string( padLen, ' ' ) << " ";
}
std::cout << tagsWrapper[i];
}
std::cout << "\n";
}
} }
}
if( config.filters().empty() ) if( config.filters().empty() )
std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl; std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
else else
@ -112,21 +65,20 @@ namespace Catch {
std::cout << "All available tags:\n"; std::cout << "All available tags:\n";
else else
std::cout << "Matching tags:\n"; std::cout << "Matching tags:\n";
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end();
std::map<std::string, int> tagCounts; std::map<std::string, int> tagCounts;
std::size_t maxTagLen = 0; std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
for( std::vector<TestCase>::const_iterator it = allTests.begin(),
for(; it != itEnd; ++it ) { itEnd = allTests.end();
it != itEnd;
++it ) {
if( matchesFilters( config.filters(), *it ) ) { if( matchesFilters( config.filters(), *it ) ) {
for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
tagItEnd = it->getTestCaseInfo().tags.end(); tagItEnd = it->getTestCaseInfo().tags.end();
tagIt != tagItEnd; tagIt != tagItEnd;
++tagIt ) { ++tagIt ) {
std::string tagName = *tagIt; std::string tagName = *tagIt;
maxTagLen = (std::max)( maxTagLen, tagName.size() );
std::map<std::string, int>::iterator countIt = tagCounts.find( tagName ); std::map<std::string, int>::iterator countIt = tagCounts.find( tagName );
if( countIt == tagCounts.end() ) if( countIt == tagCounts.end() )
tagCounts.insert( std::make_pair( tagName, 1 ) ); tagCounts.insert( std::make_pair( tagName, 1 ) );
@ -135,26 +87,18 @@ namespace Catch {
} }
} }
} }
maxTagLen +=4;
if( maxTagLen > CATCH_CONFIG_CONSOLE_WIDTH-10 )
maxTagLen = CATCH_CONFIG_CONSOLE_WIDTH-10;
for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(), countItEnd = tagCounts.end(); for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(),
countItEnd = tagCounts.end();
countIt != countItEnd; countIt != countItEnd;
++countIt ) { ++countIt ) {
std::ostringstream oss;
oss << " " << countIt->second << " ";
Text wrapper( "[" + countIt->first + "]", TextAttributes() Text wrapper( "[" + countIt->first + "]", TextAttributes()
.setIndent(2) .setInitialIndent( 0 )
.setWidth( maxTagLen ) ); .setIndent( oss.str().size() )
std::cout << wrapper; .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
std::size_t dots = 2; std::cout << oss.str() << wrapper << "\n";
if( maxTagLen > wrapper.last().size() )
dots += maxTagLen - wrapper.last().size();
{
Colour colourGuard( Colour::SecondaryText );
std::cout << std::string( dots, '.' );
}
std::cout << countIt->second
<< "\n";
} }
std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
return tagCounts.size(); return tagCounts.size();

View File

@ -14,8 +14,7 @@ namespace Catch {
// These numbers are maintained by a script // These numbers are maintained by a script
template <typename T> template <typename T>
const T LibraryVersionInfo<T>::value( 1, 0, 11, "master" ); const T LibraryVersionInfo<T>::value( 1, 0, 13, "master" );
} }
#endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED

View File

@ -41,13 +41,18 @@ namespace Catch {
virtual bool assertionEnded( AssertionStats const& _assertionStats ) { virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
AssertionResult const& result = _assertionStats.assertionResult; AssertionResult const& result = _assertionStats.assertionResult;
bool printInfoMessages = true;
// Drop out if result was successful and we're not printing those // Drop out if result was successful and we're not printing those
if( !m_config->includeSuccessfulResults() && result.isOk() ) if( !m_config->includeSuccessfulResults() && result.isOk() ) {
return false; if( result.getResultType() != ResultWas::Warning )
return false;
printInfoMessages = false;
}
lazyPrint(); lazyPrint();
AssertionPrinter printer( stream, _assertionStats ); AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
printer.print(); printer.print();
stream << std::endl; stream << std::endl;
return true; return true;
@ -105,13 +110,14 @@ namespace Catch {
class AssertionPrinter { class AssertionPrinter {
void operator= ( AssertionPrinter const& ); void operator= ( AssertionPrinter const& );
public: public:
AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats ) AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
: stream( _stream ), : stream( _stream ),
stats( _stats ), stats( _stats ),
result( _stats.assertionResult ), result( _stats.assertionResult ),
colour( Colour::None ), colour( Colour::None ),
message( result.getMessage() ), message( result.getMessage() ),
messages( _stats.infoMessages ) messages( _stats.infoMessages ),
printInfoMessages( _printInfoMessages )
{ {
switch( result.getResultType() ) { switch( result.getResultType() ) {
case ResultWas::Ok: case ResultWas::Ok:
@ -214,7 +220,9 @@ namespace Catch {
for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end(); for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
it != itEnd; it != itEnd;
++it ) { ++it ) {
stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; // If this assertion is a warning ignore any INFO messages
if( printInfoMessages || it->type != ResultWas::Info )
stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
} }
} }
void printSourceInfo() const { void printSourceInfo() const {
@ -230,6 +238,7 @@ namespace Catch {
std::string messageLabel; std::string messageLabel;
std::string message; std::string message;
std::vector<MessageInfo> messages; std::vector<MessageInfo> messages;
bool printInfoMessages;
}; };
void lazyPrint() { void lazyPrint() {
@ -315,9 +324,14 @@ namespace Catch {
} }
void printTotals( const Totals& totals ) { void printTotals( const Totals& totals ) {
if( totals.assertions.total() == 0 ) { if( totals.testCases.total() == 0 ) {
stream << "No tests ran"; stream << "No tests ran";
} }
else if( totals.assertions.total() == 0 ) {
Colour colour( Colour::Yellow );
printCounts( "test case", totals.testCases );
stream << " (no assertions)";
}
else if( totals.assertions.failed ) { else if( totals.assertions.failed ) {
Colour colour( Colour::ResultError ); Colour colour( Colour::ResultError );
printCounts( "test case", totals.testCases ); printCounts( "test case", totals.testCases );

View File

@ -373,6 +373,16 @@ ExceptionTests.cpp:<line number>: FAILED:
due to unexpected exception with message: due to unexpected exception with message:
3.14 3.14
-------------------------------------------------------------------------------
./succeeding/message
-------------------------------------------------------------------------------
MessageTests.cpp:<line number>
...............................................................................
MessageTests.cpp:<line number>:
warning:
this is a warning
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
./failing/message/info/1 ./failing/message/info/1
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -640,6 +650,16 @@ MiscTests.cpp:<line number>: FAILED:
with expansion: with expansion:
"this string contains 'abc' as a substring" equals: "something else" "this string contains 'abc' as a substring" equals: "something else"
-------------------------------------------------------------------------------
Nice descriptive name
-------------------------------------------------------------------------------
MiscTests.cpp:<line number>
...............................................................................
MiscTests.cpp:<line number>:
warning:
This one ran
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
./failing/CatchSectionInfiniteLoop ./failing/CatchSectionInfiniteLoop
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -680,6 +700,28 @@ Some information
An error An error
hello hello
hello hello
-------------------------------------------------------------------------------
./inprogress/failing/Tricky/trailing expression
-------------------------------------------------------------------------------
TrickyTests.cpp:<line number>
...............................................................................
TrickyTests.cpp:<line number>:
warning:
Uncomment the code in this test to check that it gives a sensible compiler
error
-------------------------------------------------------------------------------
./inprogress/failing/Tricky/compound lhs
-------------------------------------------------------------------------------
TrickyTests.cpp:<line number>
...............................................................................
TrickyTests.cpp:<line number>:
warning:
Uncomment the code in this test to check that it gives a sensible compiler
error
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
./failing/Tricky/non streamable type ./failing/Tricky/non streamable type
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View File

@ -1,6 +1,6 @@
/* /*
* CATCH v1.0 build 11 (master branch) * CATCH v1.0 build 13 (master branch)
* Generated: 2013-10-23 15:34:32.120349 * Generated: 2013-11-13 08:10:05.836093
* ---------------------------------------------------------- * ----------------------------------------------------------
* 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.
@ -595,6 +595,7 @@ namespace Catch {
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <limits> #include <limits>
#include <vector>
#ifdef __OBJC__ #ifdef __OBJC__
// #included from: catch_objc_arc.hpp // #included from: catch_objc_arc.hpp
@ -4573,75 +4574,29 @@ namespace Catch {
std::cout << "All available test cases:\n"; std::cout << "All available test cases:\n";
else else
std::cout << "Matching test cases:\n"; 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 ) {
if( matchesFilters( config.filters(), *it ) ) {
maxTagLen = (std::max)( it->getTestCaseInfo().tagsAsString.size(), maxTagLen );
maxNameLen = (std::max)( it->getTestCaseInfo().name.size(), maxNameLen );
}
}
// 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
--maxNameLen;
}
std::size_t matchedTests = 0; std::size_t matchedTests = 0;
for( it = allTests.begin(); it != itEnd; ++it ) { TextAttributes nameAttr, tagsAttr;
nameAttr.setInitialIndent( 2 ).setIndent( 4 );
tagsAttr.setIndent( 6 );
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end();
it != itEnd;
++it )
if( matchesFilters( config.filters(), *it ) ) { if( matchesFilters( config.filters(), *it ) ) {
matchedTests++; matchedTests++;
Text nameWrapper( it->getTestCaseInfo().name, TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
TextAttributes() Colour::Code colour = testCaseInfo.isHidden
.setWidth( maxNameLen+2 ) ? Colour::SecondaryText
.setInitialIndent(2) : Colour::None;
.setIndent(4) ); Colour colourGuard( colour );
Text tagsWrapper( it->getTestCaseInfo().tagsAsString, std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl;
TextAttributes() if( !testCaseInfo.tags.empty() )
.setWidth( maxTagLen ) std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
.setInitialIndent(0)
.setIndent( 2 ) );
for( std::size_t i = 0; i < (std::max)( nameWrapper.size(), tagsWrapper.size() ); ++i ) {
Colour::Code colour = Colour::None;
if( it->getTestCaseInfo().isHidden )
colour = Colour::SecondaryText;
std::string nameCol;
if( i < nameWrapper.size() ) {
nameCol = nameWrapper[i];
}
else {
nameCol = " ...";
colour = Colour::SecondaryText;
}
{
Colour colourGuard( colour );
std::cout << nameCol;
}
if( i < tagsWrapper.size() && !tagsWrapper[i].empty() ) {
if( i == 0 ) {
Colour colourGuard( Colour::SecondaryText );
std::cout << " " << std::string( maxNameLen - nameCol.size(), '.' ) << " ";
}
else {
std::cout << std::string( maxNameLen - nameCol.size(), ' ' ) << " ";
}
std::cout << tagsWrapper[i];
}
std::cout << "\n";
}
} }
}
if( config.filters().empty() ) if( config.filters().empty() )
std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl; std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
else else
@ -4654,21 +4609,20 @@ namespace Catch {
std::cout << "All available tags:\n"; std::cout << "All available tags:\n";
else else
std::cout << "Matching tags:\n"; std::cout << "Matching tags:\n";
std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end();
std::map<std::string, int> tagCounts; std::map<std::string, int> tagCounts;
std::size_t maxTagLen = 0; std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests();
for( std::vector<TestCase>::const_iterator it = allTests.begin(),
for(; it != itEnd; ++it ) { itEnd = allTests.end();
it != itEnd;
++it ) {
if( matchesFilters( config.filters(), *it ) ) { if( matchesFilters( config.filters(), *it ) ) {
for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(),
tagItEnd = it->getTestCaseInfo().tags.end(); tagItEnd = it->getTestCaseInfo().tags.end();
tagIt != tagItEnd; tagIt != tagItEnd;
++tagIt ) { ++tagIt ) {
std::string tagName = *tagIt; std::string tagName = *tagIt;
maxTagLen = (std::max)( maxTagLen, tagName.size() );
std::map<std::string, int>::iterator countIt = tagCounts.find( tagName ); std::map<std::string, int>::iterator countIt = tagCounts.find( tagName );
if( countIt == tagCounts.end() ) if( countIt == tagCounts.end() )
tagCounts.insert( std::make_pair( tagName, 1 ) ); tagCounts.insert( std::make_pair( tagName, 1 ) );
@ -4677,26 +4631,18 @@ namespace Catch {
} }
} }
} }
maxTagLen +=4;
if( maxTagLen > CATCH_CONFIG_CONSOLE_WIDTH-10 )
maxTagLen = CATCH_CONFIG_CONSOLE_WIDTH-10;
for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(), countItEnd = tagCounts.end(); for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(),
countItEnd = tagCounts.end();
countIt != countItEnd; countIt != countItEnd;
++countIt ) { ++countIt ) {
std::ostringstream oss;
oss << " " << countIt->second << " ";
Text wrapper( "[" + countIt->first + "]", TextAttributes() Text wrapper( "[" + countIt->first + "]", TextAttributes()
.setIndent(2) .setInitialIndent( 0 )
.setWidth( maxTagLen ) ); .setIndent( oss.str().size() )
std::cout << wrapper; .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
std::size_t dots = 2; std::cout << oss.str() << wrapper << "\n";
if( maxTagLen > wrapper.last().size() )
dots += maxTagLen - wrapper.last().size();
{
Colour colourGuard( Colour::SecondaryText );
std::cout << std::string( dots, '.' );
}
std::cout << countIt->second
<< "\n";
} }
std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
return tagCounts.size(); return tagCounts.size();
@ -6371,7 +6317,7 @@ namespace Catch {
namespace Catch { namespace Catch {
// These numbers are maintained by a script // These numbers are maintained by a script
Version libraryVersion( 1, 0, 11, "master" ); Version libraryVersion( 1, 0, 13, "master" );
} }
// #included from: catch_text.hpp // #included from: catch_text.hpp
@ -7294,13 +7240,18 @@ namespace Catch {
virtual bool assertionEnded( AssertionStats const& _assertionStats ) { virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
AssertionResult const& result = _assertionStats.assertionResult; AssertionResult const& result = _assertionStats.assertionResult;
bool printInfoMessages = true;
// Drop out if result was successful and we're not printing those // Drop out if result was successful and we're not printing those
if( !m_config->includeSuccessfulResults() && result.isOk() ) if( !m_config->includeSuccessfulResults() && result.isOk() ) {
return false; if( result.getResultType() != ResultWas::Warning )
return false;
printInfoMessages = false;
}
lazyPrint(); lazyPrint();
AssertionPrinter printer( stream, _assertionStats ); AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
printer.print(); printer.print();
stream << std::endl; stream << std::endl;
return true; return true;
@ -7358,13 +7309,14 @@ namespace Catch {
class AssertionPrinter { class AssertionPrinter {
void operator= ( AssertionPrinter const& ); void operator= ( AssertionPrinter const& );
public: public:
AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats ) AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
: stream( _stream ), : stream( _stream ),
stats( _stats ), stats( _stats ),
result( _stats.assertionResult ), result( _stats.assertionResult ),
colour( Colour::None ), colour( Colour::None ),
message( result.getMessage() ), message( result.getMessage() ),
messages( _stats.infoMessages ) messages( _stats.infoMessages ),
printInfoMessages( _printInfoMessages )
{ {
switch( result.getResultType() ) { switch( result.getResultType() ) {
case ResultWas::Ok: case ResultWas::Ok:
@ -7467,7 +7419,9 @@ namespace Catch {
for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end(); for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
it != itEnd; it != itEnd;
++it ) { ++it ) {
stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; // If this assertion is a warning ignore any INFO messages
if( printInfoMessages || it->type != ResultWas::Info )
stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
} }
} }
void printSourceInfo() const { void printSourceInfo() const {
@ -7483,6 +7437,7 @@ namespace Catch {
std::string messageLabel; std::string messageLabel;
std::string message; std::string message;
std::vector<MessageInfo> messages; std::vector<MessageInfo> messages;
bool printInfoMessages;
}; };
void lazyPrint() { void lazyPrint() {
@ -7568,9 +7523,14 @@ namespace Catch {
} }
void printTotals( const Totals& totals ) { void printTotals( const Totals& totals ) {
if( totals.assertions.total() == 0 ) { if( totals.testCases.total() == 0 ) {
stream << "No tests ran"; stream << "No tests ran";
} }
else if( totals.assertions.total() == 0 ) {
Colour colour( Colour::Yellow );
printCounts( "test case", totals.testCases );
stream << " (no assertions)";
}
else if( totals.assertions.failed ) { else if( totals.assertions.failed ) {
Colour colour( Colour::ResultError ); Colour colour( Colour::ResultError );
printCounts( "test case", totals.testCases ); printCounts( "test case", totals.testCases );