catch2/include/reporters/catch_reporter_compact.cpp
Martin Hořeňovský d6c7392b24 Add a new reporter customization point: reporting all assertions
By opting the JUnit and XML reporters into it, we no longer run
into problem where they underreport the results without `-s` flag.

Related to #1264, #1267, #1310
2018-07-14 20:51:02 +02:00

293 lines
9.9 KiB
C++

/*
* Created by Martin on 2017-11-14.
*
* 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)
*/
#include "catch_reporter_compact.h"
#include "../internal/catch_reporter_registrars.hpp"
#include "internal/catch_console_colour.h"
namespace {
#ifdef CATCH_PLATFORM_MAC
const char* failedString() { return "FAILED"; }
const char* passedString() { return "PASSED"; }
#else
const char* failedString() { return "failed"; }
const char* passedString() { return "passed"; }
#endif
// Colour::LightGrey
Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
std::string bothOrAll( std::size_t count ) {
return count == 1 ? std::string() :
count == 2 ? "both " : "all " ;
}
} // anon namespace
namespace Catch {
namespace {
// Colour, message variants:
// - white: No tests ran.
// - red: Failed [both/all] N test cases, failed [both/all] M assertions.
// - white: Passed [both/all] N test cases (no assertions).
// - red: Failed N tests cases, failed M assertions.
// - green: Passed [both/all] N tests cases with M assertions.
void printTotals(std::ostream& out, const Totals& totals) {
if (totals.testCases.total() == 0) {
out << "No tests ran.";
} else if (totals.testCases.failed == totals.testCases.total()) {
Colour colour(Colour::ResultError);
const std::string qualify_assertions_failed =
totals.assertions.failed == totals.assertions.total() ?
bothOrAll(totals.assertions.failed) : std::string();
out <<
"Failed " << bothOrAll(totals.testCases.failed)
<< pluralise(totals.testCases.failed, "test case") << ", "
"failed " << qualify_assertions_failed <<
pluralise(totals.assertions.failed, "assertion") << '.';
} else if (totals.assertions.total() == 0) {
out <<
"Passed " << bothOrAll(totals.testCases.total())
<< pluralise(totals.testCases.total(), "test case")
<< " (no assertions).";
} else if (totals.assertions.failed) {
Colour colour(Colour::ResultError);
out <<
"Failed " << pluralise(totals.testCases.failed, "test case") << ", "
"failed " << pluralise(totals.assertions.failed, "assertion") << '.';
} else {
Colour colour(Colour::ResultSuccess);
out <<
"Passed " << bothOrAll(totals.testCases.passed)
<< pluralise(totals.testCases.passed, "test case") <<
" with " << pluralise(totals.assertions.passed, "assertion") << '.';
}
}
// Implementation of CompactReporter formatting
class AssertionPrinter {
public:
AssertionPrinter& operator= (AssertionPrinter const&) = delete;
AssertionPrinter(AssertionPrinter const&) = delete;
AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
: stream(_stream)
, result(_stats.assertionResult)
, messages(_stats.infoMessages)
, itMessage(_stats.infoMessages.begin())
, printInfoMessages(_printInfoMessages) {}
void print() {
printSourceInfo();
itMessage = messages.begin();
switch (result.getResultType()) {
case ResultWas::Ok:
printResultType(Colour::ResultSuccess, passedString());
printOriginalExpression();
printReconstructedExpression();
if (!result.hasExpression())
printRemainingMessages(Colour::None);
else
printRemainingMessages();
break;
case ResultWas::ExpressionFailed:
if (result.isOk())
printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok"));
else
printResultType(Colour::Error, failedString());
printOriginalExpression();
printReconstructedExpression();
printRemainingMessages();
break;
case ResultWas::ThrewException:
printResultType(Colour::Error, failedString());
printIssue("unexpected exception with message:");
printMessage();
printExpressionWas();
printRemainingMessages();
break;
case ResultWas::FatalErrorCondition:
printResultType(Colour::Error, failedString());
printIssue("fatal error condition with message:");
printMessage();
printExpressionWas();
printRemainingMessages();
break;
case ResultWas::DidntThrowException:
printResultType(Colour::Error, failedString());
printIssue("expected exception, got none");
printExpressionWas();
printRemainingMessages();
break;
case ResultWas::Info:
printResultType(Colour::None, "info");
printMessage();
printRemainingMessages();
break;
case ResultWas::Warning:
printResultType(Colour::None, "warning");
printMessage();
printRemainingMessages();
break;
case ResultWas::ExplicitFailure:
printResultType(Colour::Error, failedString());
printIssue("explicitly");
printRemainingMessages(Colour::None);
break;
// These cases are here to prevent compiler warnings
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
printResultType(Colour::Error, "** internal error **");
break;
}
}
private:
void printSourceInfo() const {
Colour colourGuard(Colour::FileName);
stream << result.getSourceInfo() << ':';
}
void printResultType(Colour::Code colour, std::string const& passOrFail) const {
if (!passOrFail.empty()) {
{
Colour colourGuard(colour);
stream << ' ' << passOrFail;
}
stream << ':';
}
}
void printIssue(std::string const& issue) const {
stream << ' ' << issue;
}
void printExpressionWas() {
if (result.hasExpression()) {
stream << ';';
{
Colour colour(dimColour());
stream << " expression was:";
}
printOriginalExpression();
}
}
void printOriginalExpression() const {
if (result.hasExpression()) {
stream << ' ' << result.getExpression();
}
}
void printReconstructedExpression() const {
if (result.hasExpandedExpression()) {
{
Colour colour(dimColour());
stream << " for: ";
}
stream << result.getExpandedExpression();
}
}
void printMessage() {
if (itMessage != messages.end()) {
stream << " '" << itMessage->message << '\'';
++itMessage;
}
}
void printRemainingMessages(Colour::Code colour = dimColour()) {
if (itMessage == messages.end())
return;
// using messages.end() directly yields (or auto) compilation error:
std::vector<MessageInfo>::const_iterator itEnd = messages.end();
const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
{
Colour colourGuard(colour);
stream << " with " << pluralise(N, "message") << ':';
}
for (; itMessage != itEnd; ) {
// If this assertion is a warning ignore any INFO messages
if (printInfoMessages || itMessage->type != ResultWas::Info) {
stream << " '" << itMessage->message << '\'';
if (++itMessage != itEnd) {
Colour colourGuard(dimColour());
stream << " and";
}
}
}
}
private:
std::ostream& stream;
AssertionResult const& result;
std::vector<MessageInfo> messages;
std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages;
};
} // anon namespace
std::string CompactReporter::getDescription() {
return "Reports test results on a single line, suitable for IDEs";
}
ReporterPreferences CompactReporter::getPreferences() const {
return m_reporterPrefs;
}
void CompactReporter::noMatchingTestCases( std::string const& spec ) {
stream << "No test cases matched '" << spec << '\'' << std::endl;
}
void CompactReporter::assertionStarting( AssertionInfo const& ) {}
bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
AssertionResult const& result = _assertionStats.assertionResult;
bool printInfoMessages = true;
// Drop out if result was successful and we're not printing those
if( !m_config->includeSuccessfulResults() && result.isOk() ) {
if( result.getResultType() != ResultWas::Warning )
return false;
printInfoMessages = false;
}
AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
printer.print();
stream << std::endl;
return true;
}
void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
if (m_config->showDurations() == ShowDurations::Always) {
stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
}
}
void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
printTotals( stream, _testRunStats.totals );
stream << '\n' << std::endl;
StreamingReporterBase::testRunEnded( _testRunStats );
}
CompactReporter::~CompactReporter() {}
CATCH_REGISTER_REPORTER( "compact", CompactReporter )
} // end namespace Catch