Provide ConsoleReporter declaration with EXTERNAL_INTERFACES

Related to #991
This commit is contained in:
Martin Hořeňovský 2017-11-14 20:42:58 +01:00
parent a096e4b3f2
commit 98d4c49d1c
4 changed files with 673 additions and 609 deletions

View File

@ -248,6 +248,7 @@ set(REPORTER_HEADERS
${HEADER_DIR}/reporters/catch_reporter_automake.hpp ${HEADER_DIR}/reporters/catch_reporter_automake.hpp
${HEADER_DIR}/reporters/catch_reporter_bases.hpp ${HEADER_DIR}/reporters/catch_reporter_bases.hpp
${HEADER_DIR}/reporters/catch_reporter_compact.h ${HEADER_DIR}/reporters/catch_reporter_compact.h
${HEADER_DIR}/reporters/catch_reporter_console.h
${HEADER_DIR}/reporters/catch_reporter_junit.h ${HEADER_DIR}/reporters/catch_reporter_junit.h
${HEADER_DIR}/reporters/catch_reporter_multi.h ${HEADER_DIR}/reporters/catch_reporter_multi.h
${HEADER_DIR}/reporters/catch_reporter_tap.hpp ${HEADER_DIR}/reporters/catch_reporter_tap.hpp

View File

@ -13,6 +13,7 @@
// Allow users to base their work off existing reporters // Allow users to base their work off existing reporters
#include "../reporters/catch_reporter_compact.h" #include "../reporters/catch_reporter_compact.h"
#include "../reporters/catch_reporter_console.h"
#include "../reporters/catch_reporter_junit.h" #include "../reporters/catch_reporter_junit.h"
#include "../reporters/catch_reporter_xml.h" #include "../reporters/catch_reporter_xml.h"

View File

@ -6,7 +6,7 @@
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/ */
#include "catch_reporter_bases.hpp" #include "catch_reporter_console.h"
#include "../internal/catch_reporter_registrars.hpp" #include "../internal/catch_reporter_registrars.hpp"
#include "internal/catch_console_colour.h" #include "internal/catch_console_colour.h"
@ -28,6 +28,147 @@
namespace Catch { namespace Catch {
namespace { namespace {
// Formatter impl for ConsoleReporter
class AssertionPrinter {
public:
AssertionPrinter& operator= (AssertionPrinter const&) = delete;
AssertionPrinter(AssertionPrinter const&) = delete;
AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
: stream(_stream),
stats(_stats),
result(_stats.assertionResult),
colour(Colour::None),
message(result.getMessage()),
messages(_stats.infoMessages),
printInfoMessages(_printInfoMessages) {
switch (result.getResultType()) {
case ResultWas::Ok:
colour = Colour::Success;
passOrFail = "PASSED";
//if( result.hasMessage() )
if (_stats.infoMessages.size() == 1)
messageLabel = "with message";
if (_stats.infoMessages.size() > 1)
messageLabel = "with messages";
break;
case ResultWas::ExpressionFailed:
if (result.isOk()) {
colour = Colour::Success;
passOrFail = "FAILED - but was ok";
} else {
colour = Colour::Error;
passOrFail = "FAILED";
}
if (_stats.infoMessages.size() == 1)
messageLabel = "with message";
if (_stats.infoMessages.size() > 1)
messageLabel = "with messages";
break;
case ResultWas::ThrewException:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "due to unexpected exception with ";
if (_stats.infoMessages.size() == 1)
messageLabel += "message";
if (_stats.infoMessages.size() > 1)
messageLabel += "messages";
break;
case ResultWas::FatalErrorCondition:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "due to a fatal error condition";
break;
case ResultWas::DidntThrowException:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "because no exception was thrown where one was expected";
break;
case ResultWas::Info:
messageLabel = "info";
break;
case ResultWas::Warning:
messageLabel = "warning";
break;
case ResultWas::ExplicitFailure:
passOrFail = "FAILED";
colour = Colour::Error;
if (_stats.infoMessages.size() == 1)
messageLabel = "explicitly with message";
if (_stats.infoMessages.size() > 1)
messageLabel = "explicitly with messages";
break;
// These cases are here to prevent compiler warnings
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
passOrFail = "** internal error **";
colour = Colour::Error;
break;
}
}
void print() const {
printSourceInfo();
if (stats.totals.assertions.total() > 0) {
if (result.isOk())
stream << '\n';
printResultType();
printOriginalExpression();
printReconstructedExpression();
} else {
stream << '\n';
}
printMessage();
}
private:
void printResultType() const {
if (!passOrFail.empty()) {
Colour colourGuard(colour);
stream << passOrFail << ":\n";
}
}
void printOriginalExpression() const {
if (result.hasExpression()) {
Colour colourGuard(Colour::OriginalExpression);
stream << " ";
stream << result.getExpressionInMacro();
stream << '\n';
}
}
void printReconstructedExpression() const {
if (result.hasExpandedExpression()) {
stream << "with expansion:\n";
Colour colourGuard(Colour::ReconstructedExpression);
stream << Column(result.getExpandedExpression()).indent(2) << '\n';
}
}
void printMessage() const {
if (!messageLabel.empty())
stream << messageLabel << ':' << '\n';
for (auto const& msg : messages) {
// If this assertion is a warning ignore any INFO messages
if (printInfoMessages || msg.type != ResultWas::Info)
stream << Column(msg.message).indent(2) << '\n';
}
}
void printSourceInfo() const {
Colour colourGuard(Colour::FileName);
stream << result.getSourceInfo() << ": ";
}
std::ostream& stream;
AssertionStats const& stats;
AssertionResult const& result;
Colour::Code colour;
std::string passOrFail;
std::string messageLabel;
std::string message;
std::vector<MessageInfo> messages;
bool printInfoMessages;
};
std::size_t makeRatio(std::size_t number, std::size_t total) { std::size_t makeRatio(std::size_t number, std::size_t total) {
std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
return (ratio == 0 && number > 0) ? 1 : ratio; return (ratio == 0 && number > 0) ? 1 : ratio;
@ -51,6 +192,79 @@ namespace Catch {
struct ColumnBreak {}; struct ColumnBreak {};
struct RowBreak {}; struct RowBreak {};
class Duration {
enum class Unit {
Auto,
Nanoseconds,
Microseconds,
Milliseconds,
Seconds,
Minutes
};
static const uint64_t s_nanosecondsInAMicrosecond = 1000;
static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
uint64_t m_inNanoseconds;
Unit m_units;
public:
Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
: m_inNanoseconds(inNanoseconds),
m_units(units) {
if (m_units == Unit::Auto) {
if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
m_units = Unit::Nanoseconds;
else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
m_units = Unit::Microseconds;
else if (m_inNanoseconds < s_nanosecondsInASecond)
m_units = Unit::Milliseconds;
else if (m_inNanoseconds < s_nanosecondsInAMinute)
m_units = Unit::Seconds;
else
m_units = Unit::Minutes;
}
}
auto value() const -> double {
switch (m_units) {
case Unit::Microseconds:
return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
case Unit::Milliseconds:
return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
case Unit::Seconds:
return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
case Unit::Minutes:
return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
default:
return static_cast<double>(m_inNanoseconds);
}
}
auto unitsAsString() const -> std::string {
switch (m_units) {
case Unit::Nanoseconds:
return "ns";
case Unit::Microseconds:
return "µs";
case Unit::Milliseconds:
return "ms";
case Unit::Seconds:
return "s";
case Unit::Minutes:
return "m";
default:
return "** internal error **";
}
}
friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
return os << duration.value() << " " << duration.unitsAsString();
}
};
} // end anon namespace
class TablePrinter { class TablePrinter {
std::ostream& m_os; std::ostream& m_os;
std::vector<ColumnInfo> m_columnInfos; std::vector<ColumnInfo> m_columnInfos;
@ -61,8 +275,7 @@ namespace Catch {
public: public:
TablePrinter(std::ostream& os, std::vector<ColumnInfo> const& columnInfos) TablePrinter(std::ostream& os, std::vector<ColumnInfo> const& columnInfos)
: m_os(os), : m_os(os),
m_columnInfos( columnInfos ) m_columnInfos(columnInfos) {}
{}
auto columnInfos() const -> std::vector<ColumnInfo> const& { auto columnInfos() const -> std::vector<ColumnInfo> const& {
return m_columnInfos; return m_columnInfos;
@ -124,106 +337,27 @@ namespace Catch {
} }
}; };
class Duration { ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
enum class Unit {
Auto,
Nanoseconds,
Microseconds,
Milliseconds,
Seconds,
Minutes
};
static const uint64_t s_nanosecondsInAMicrosecond = 1000;
static const uint64_t s_nanosecondsInAMillisecond = 1000*s_nanosecondsInAMicrosecond;
static const uint64_t s_nanosecondsInASecond = 1000*s_nanosecondsInAMillisecond;
static const uint64_t s_nanosecondsInAMinute = 60*s_nanosecondsInASecond;
uint64_t m_inNanoseconds;
Unit m_units;
public:
Duration( uint64_t inNanoseconds, Unit units = Unit::Auto )
: m_inNanoseconds( inNanoseconds ),
m_units( units )
{
if( m_units == Unit::Auto ) {
if( m_inNanoseconds < s_nanosecondsInAMicrosecond )
m_units = Unit::Nanoseconds;
else if( m_inNanoseconds < s_nanosecondsInAMillisecond )
m_units = Unit::Microseconds;
else if( m_inNanoseconds < s_nanosecondsInASecond )
m_units = Unit::Milliseconds;
else if( m_inNanoseconds < s_nanosecondsInAMinute )
m_units = Unit::Seconds;
else
m_units = Unit::Minutes;
}
}
auto value() const -> double {
switch( m_units ) {
case Unit::Microseconds:
return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMicrosecond );
case Unit::Milliseconds:
return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMillisecond );
case Unit::Seconds:
return m_inNanoseconds / static_cast<double>( s_nanosecondsInASecond );
case Unit::Minutes:
return m_inNanoseconds / static_cast<double>( s_nanosecondsInAMinute );
default:
return static_cast<double>( m_inNanoseconds );
}
}
auto unitsAsString() const -> std::string {
switch( m_units ) {
case Unit::Nanoseconds:
return "ns";
case Unit::Microseconds:
return "µs";
case Unit::Milliseconds:
return "ms";
case Unit::Seconds:
return "s";
case Unit::Minutes:
return "m";
default:
return "** internal error **";
}
}
friend auto operator << ( std::ostream& os, Duration const& duration ) -> std::ostream& {
return os << duration.value() << " " << duration.unitsAsString();
}
};
} // end anon namespace
struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> {
TablePrinter m_tablePrinter;
ConsoleReporter( ReporterConfig const& config )
: StreamingReporterBase(config), : StreamingReporterBase(config),
m_tablePrinter( config.stream(), m_tablePrinter(new TablePrinter(config.stream(),
{ {
{ "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
{ "iters", 8, ColumnInfo::Right }, { "iters", 8, ColumnInfo::Right },
{ "elapsed ns", 14, ColumnInfo::Right }, { "elapsed ns", 14, ColumnInfo::Right },
{ "average", 14, ColumnInfo::Right } { "average", 14, ColumnInfo::Right }
} ) })) {}
{} ConsoleReporter::~ConsoleReporter() {}
~ConsoleReporter() override; std::string ConsoleReporter::getDescription() {
static std::string getDescription() {
return "Reports test results as plain lines of text"; return "Reports test results as plain lines of text";
} }
void noMatchingTestCases( std::string const& spec ) override { void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
stream << "No test cases matched '" << spec << '\'' << std::endl; stream << "No test cases matched '" << spec << '\'' << std::endl;
} }
void assertionStarting( AssertionInfo const& ) override { void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
}
bool assertionEnded( AssertionStats const& _assertionStats ) override { bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
AssertionResult const& result = _assertionStats.assertionResult; AssertionResult const& result = _assertionStats.assertionResult;
bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
@ -240,12 +374,12 @@ namespace Catch {
return true; return true;
} }
void sectionStarting( SectionInfo const& _sectionInfo ) override { void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
m_headerPrinted = false; m_headerPrinted = false;
StreamingReporterBase::sectionStarting(_sectionInfo); StreamingReporterBase::sectionStarting(_sectionInfo);
} }
void sectionEnded( SectionStats const& _sectionStats ) override { void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
m_tablePrinter.close(); m_tablePrinter->close();
if (_sectionStats.missingAssertions) { if (_sectionStats.missingAssertions) {
lazyPrint(); lazyPrint();
Colour colour(Colour::ResultError); Colour colour(Colour::ResultError);
@ -265,35 +399,35 @@ namespace Catch {
} }
void benchmarkStarting( BenchmarkInfo const& info ) override { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
lazyPrintWithoutClosingBenchmarkTable(); lazyPrintWithoutClosingBenchmarkTable();
auto nameCol = Column( info.name ).width( m_tablePrinter.columnInfos()[0].width-2 ); auto nameCol = Column(info.name).width(m_tablePrinter->columnInfos()[0].width - 2);
bool firstLine = true; bool firstLine = true;
for (auto line : nameCol) { for (auto line : nameCol) {
if (!firstLine) if (!firstLine)
m_tablePrinter << ColumnBreak() << ColumnBreak() << ColumnBreak(); (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
else else
firstLine = false; firstLine = false;
m_tablePrinter << line << ColumnBreak(); (*m_tablePrinter) << line << ColumnBreak();
} }
} }
void benchmarkEnded( BenchmarkStats const& stats ) override { void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
m_tablePrinter (*m_tablePrinter)
<< stats.iterations << ColumnBreak() << stats.iterations << ColumnBreak()
<< stats.elapsedTimeInNanoseconds << ColumnBreak() << stats.elapsedTimeInNanoseconds << ColumnBreak()
<< average << ColumnBreak(); << average << ColumnBreak();
} }
void testCaseEnded( TestCaseStats const& _testCaseStats ) override { void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
m_tablePrinter.close(); m_tablePrinter->close();
StreamingReporterBase::testCaseEnded(_testCaseStats); StreamingReporterBase::testCaseEnded(_testCaseStats);
m_headerPrinted = false; m_headerPrinted = false;
} }
void testGroupEnded( TestGroupStats const& _testGroupStats ) override { void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
if (currentGroupInfo.used) { if (currentGroupInfo.used) {
printSummaryDivider(); printSummaryDivider();
stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
@ -302,164 +436,20 @@ namespace Catch {
} }
StreamingReporterBase::testGroupEnded(_testGroupStats); StreamingReporterBase::testGroupEnded(_testGroupStats);
} }
void testRunEnded( TestRunStats const& _testRunStats ) override { void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
printTotalsDivider(_testRunStats.totals); printTotalsDivider(_testRunStats.totals);
printTotals(_testRunStats.totals); printTotals(_testRunStats.totals);
stream << std::endl; stream << std::endl;
StreamingReporterBase::testRunEnded(_testRunStats); StreamingReporterBase::testRunEnded(_testRunStats);
} }
private: void ConsoleReporter::lazyPrint() {
class AssertionPrinter { m_tablePrinter->close();
public:
AssertionPrinter& operator= ( AssertionPrinter const& ) = delete;
AssertionPrinter( AssertionPrinter const& ) = delete;
AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
: stream( _stream ),
stats( _stats ),
result( _stats.assertionResult ),
colour( Colour::None ),
message( result.getMessage() ),
messages( _stats.infoMessages ),
printInfoMessages( _printInfoMessages )
{
switch( result.getResultType() ) {
case ResultWas::Ok:
colour = Colour::Success;
passOrFail = "PASSED";
//if( result.hasMessage() )
if( _stats.infoMessages.size() == 1 )
messageLabel = "with message";
if( _stats.infoMessages.size() > 1 )
messageLabel = "with messages";
break;
case ResultWas::ExpressionFailed:
if( result.isOk() ) {
colour = Colour::Success;
passOrFail = "FAILED - but was ok";
}
else {
colour = Colour::Error;
passOrFail = "FAILED";
}
if( _stats.infoMessages.size() == 1 )
messageLabel = "with message";
if( _stats.infoMessages.size() > 1 )
messageLabel = "with messages";
break;
case ResultWas::ThrewException:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "due to unexpected exception with ";
if (_stats.infoMessages.size() == 1)
messageLabel += "message";
if (_stats.infoMessages.size() > 1)
messageLabel += "messages";
break;
case ResultWas::FatalErrorCondition:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "due to a fatal error condition";
break;
case ResultWas::DidntThrowException:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "because no exception was thrown where one was expected";
break;
case ResultWas::Info:
messageLabel = "info";
break;
case ResultWas::Warning:
messageLabel = "warning";
break;
case ResultWas::ExplicitFailure:
passOrFail = "FAILED";
colour = Colour::Error;
if( _stats.infoMessages.size() == 1 )
messageLabel = "explicitly with message";
if( _stats.infoMessages.size() > 1 )
messageLabel = "explicitly with messages";
break;
// These cases are here to prevent compiler warnings
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
passOrFail = "** internal error **";
colour = Colour::Error;
break;
}
}
void print() const {
printSourceInfo();
if( stats.totals.assertions.total() > 0 ) {
if( result.isOk() )
stream << '\n';
printResultType();
printOriginalExpression();
printReconstructedExpression();
}
else {
stream << '\n';
}
printMessage();
}
private:
void printResultType() const {
if( !passOrFail.empty() ) {
Colour colourGuard( colour );
stream << passOrFail << ":\n";
}
}
void printOriginalExpression() const {
if( result.hasExpression() ) {
Colour colourGuard( Colour::OriginalExpression );
stream << " ";
stream << result.getExpressionInMacro();
stream << '\n';
}
}
void printReconstructedExpression() const {
if( result.hasExpandedExpression() ) {
stream << "with expansion:\n";
Colour colourGuard( Colour::ReconstructedExpression );
stream << Column( result.getExpandedExpression() ).indent(2) << '\n';
}
}
void printMessage() const {
if( !messageLabel.empty() )
stream << messageLabel << ':' << '\n';
for( auto const& msg : messages ) {
// If this assertion is a warning ignore any INFO messages
if( printInfoMessages || msg.type != ResultWas::Info )
stream << Column( msg.message ).indent(2) << '\n';
}
}
void printSourceInfo() const {
Colour colourGuard( Colour::FileName );
stream << result.getSourceInfo() << ": ";
}
std::ostream& stream;
AssertionStats const& stats;
AssertionResult const& result;
Colour::Code colour;
std::string passOrFail;
std::string messageLabel;
std::string message;
std::vector<MessageInfo> messages;
bool printInfoMessages;
};
void lazyPrint() {
m_tablePrinter.close();
lazyPrintWithoutClosingBenchmarkTable(); lazyPrintWithoutClosingBenchmarkTable();
} }
void lazyPrintWithoutClosingBenchmarkTable() { void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
if (!currentTestRunInfo.used) if (!currentTestRunInfo.used)
lazyPrintRunInfo(); lazyPrintRunInfo();
@ -471,7 +461,7 @@ namespace Catch {
m_headerPrinted = true; m_headerPrinted = true;
} }
} }
void lazyPrintRunInfo() { void ConsoleReporter::lazyPrintRunInfo() {
stream << '\n' << getLineOfChars<'~'>() << '\n'; stream << '\n' << getLineOfChars<'~'>() << '\n';
Colour colour(Colour::SecondaryText); Colour colour(Colour::SecondaryText);
stream << currentTestRunInfo->name stream << currentTestRunInfo->name
@ -483,13 +473,13 @@ namespace Catch {
currentTestRunInfo.used = true; currentTestRunInfo.used = true;
} }
void lazyPrintGroupInfo() { void ConsoleReporter::lazyPrintGroupInfo() {
if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
printClosedHeader("Group: " + currentGroupInfo->name); printClosedHeader("Group: " + currentGroupInfo->name);
currentGroupInfo.used = true; currentGroupInfo.used = true;
} }
} }
void printTestCaseAndSectionHeader() { void ConsoleReporter::printTestCaseAndSectionHeader() {
assert(!m_sectionStack.empty()); assert(!m_sectionStack.empty());
printOpenHeader(currentTestCaseInfo->name); printOpenHeader(currentTestCaseInfo->name);
@ -513,11 +503,11 @@ namespace Catch {
stream << getLineOfChars<'.'>() << '\n' << std::endl; stream << getLineOfChars<'.'>() << '\n' << std::endl;
} }
void printClosedHeader( std::string const& _name ) { void ConsoleReporter::printClosedHeader(std::string const& _name) {
printOpenHeader(_name); printOpenHeader(_name);
stream << getLineOfChars<'.'>() << '\n'; stream << getLineOfChars<'.'>() << '\n';
} }
void printOpenHeader( std::string const& _name ) { void ConsoleReporter::printOpenHeader(std::string const& _name) {
stream << getLineOfChars<'-'>() << '\n'; stream << getLineOfChars<'-'>() << '\n';
{ {
Colour colourGuard(Colour::Headers); Colour colourGuard(Colour::Headers);
@ -527,7 +517,7 @@ namespace Catch {
// if string has a : in first line will set indent to follow it on // if string has a : in first line will set indent to follow it on
// subsequent lines // subsequent lines
void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
std::size_t i = _string.find(": "); std::size_t i = _string.find(": ");
if (i != std::string::npos) if (i != std::string::npos)
i += 2; i += 2;
@ -540,8 +530,7 @@ namespace Catch {
SummaryColumn(std::string const& _label, Colour::Code _colour) SummaryColumn(std::string const& _label, Colour::Code _colour)
: label(_label), : label(_label),
colour( _colour ) colour(_colour) {}
{}
SummaryColumn addRow(std::size_t count) { SummaryColumn addRow(std::size_t count) {
ReusableStringStream rss; ReusableStringStream rss;
rss << count; rss << count;
@ -562,18 +551,16 @@ namespace Catch {
}; };
void printTotals( Totals const& totals ) { void ConsoleReporter::printTotals(Totals const& totals) {
if (totals.testCases.total() == 0) { if (totals.testCases.total() == 0) {
stream << Colour(Colour::Warning) << "No tests ran\n"; stream << Colour(Colour::Warning) << "No tests ran\n";
} } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
stream << Colour(Colour::ResultSuccess) << "All tests passed"; stream << Colour(Colour::ResultSuccess) << "All tests passed";
stream << " (" stream << " ("
<< pluralise(totals.assertions.passed, "assertion") << " in " << pluralise(totals.assertions.passed, "assertion") << " in "
<< pluralise(totals.testCases.passed, "test case") << ')' << pluralise(totals.testCases.passed, "test case") << ')'
<< '\n'; << '\n';
} } else {
else {
std::vector<SummaryColumn> columns; std::vector<SummaryColumn> columns;
columns.push_back(SummaryColumn("", Colour::None) columns.push_back(SummaryColumn("", Colour::None)
@ -593,7 +580,7 @@ namespace Catch {
printSummaryRow("assertions", columns, 1); printSummaryRow("assertions", columns, 1);
} }
} }
void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) { void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
for (auto col : cols) { for (auto col : cols) {
std::string value = col.rows[row]; std::string value = col.rows[row];
if (col.label.empty()) { if (col.label.empty()) {
@ -602,8 +589,7 @@ namespace Catch {
stream << value; stream << value;
else else
stream << Colour(Colour::Warning) << "- none -"; stream << Colour(Colour::Warning) << "- none -";
} } else if (value != "0") {
else if( value != "0" ) {
stream << Colour(Colour::LightGrey) << " | "; stream << Colour(Colour::LightGrey) << " | ";
stream << Colour(col.colour) stream << Colour(col.colour)
<< value << ' ' << col.label; << value << ' ' << col.label;
@ -612,7 +598,7 @@ namespace Catch {
stream << '\n'; stream << '\n';
} }
void printTotalsDivider( Totals const& totals ) { void ConsoleReporter::printTotalsDivider(Totals const& totals) {
if (totals.testCases.total() > 0) { if (totals.testCases.total() > 0) {
std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
@ -628,24 +614,17 @@ namespace Catch {
stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
else else
stream << Colour(Colour::Success) << std::string(passedRatio, '='); stream << Colour(Colour::Success) << std::string(passedRatio, '=');
} } else {
else {
stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
} }
stream << '\n'; stream << '\n';
} }
void printSummaryDivider() { void ConsoleReporter::printSummaryDivider() {
stream << getLineOfChars<'-'>() << '\n'; stream << getLineOfChars<'-'>() << '\n';
} }
private:
bool m_headerPrinted = false;
};
CATCH_REGISTER_REPORTER("console", ConsoleReporter) CATCH_REGISTER_REPORTER("console", ConsoleReporter)
ConsoleReporter::~ConsoleReporter() {}
} // end namespace Catch } // end namespace Catch
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -0,0 +1,83 @@
/*
* Created by Phil on 5/12/2012.
* Copyright 2012 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_REPORTER_CONSOLE_H_INCLUDED
#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_H_INCLUDED
#include "catch_reporter_bases.hpp"
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
// Note that 4062 (not all labels are handled
// and default is missing) is enabled
#endif
namespace Catch {
// Fwd decls
struct SummaryColumn;
class TablePrinter;
struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> {
std::unique_ptr<TablePrinter> m_tablePrinter;
ConsoleReporter(ReporterConfig const& config);
~ConsoleReporter() override;
static std::string getDescription();
void noMatchingTestCases(std::string const& spec) override;
void assertionStarting(AssertionInfo const&) override;
bool assertionEnded(AssertionStats const& _assertionStats) override;
void sectionStarting(SectionInfo const& _sectionInfo) override;
void sectionEnded(SectionStats const& _sectionStats) override;
void benchmarkStarting(BenchmarkInfo const& info) override;
void benchmarkEnded(BenchmarkStats const& stats) override;
void testCaseEnded(TestCaseStats const& _testCaseStats) override;
void testGroupEnded(TestGroupStats const& _testGroupStats) override;
void testRunEnded(TestRunStats const& _testRunStats) override;
private:
void lazyPrint();
void lazyPrintWithoutClosingBenchmarkTable();
void lazyPrintRunInfo();
void lazyPrintGroupInfo();
void printTestCaseAndSectionHeader();
void printClosedHeader(std::string const& _name);
void printOpenHeader(std::string const& _name);
// if string has a : in first line will set indent to follow it on
// subsequent lines
void printHeaderString(std::string const& _string, std::size_t indent = 0);
void printTotals(Totals const& totals);
void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
void printTotalsDivider(Totals const& totals);
void printSummaryDivider();
private:
bool m_headerPrinted = false;
};
} // end namespace Catch
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif // TWOBLUECUBES_CATCH_REPORTER_CONSOLE_H_INCLUDED