mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 19:53:28 +01:00
46fde0c597
A test runner already has a --durations option to print durations. However, this isn't entirely satisfactory. When there are many tests, this produces output spam which makes it hard to find the test failure output. Nevertheless, it is helpful to be informed of tests which are unusually slow. Therefore, introduce a new option --min-duration that causes all durations above a certain threshold to be printed. This allows slow tests to be visible without mentioning every test.
295 lines
9.9 KiB
C++
295 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;
|
|
|
|
const auto itEnd = messages.cend();
|
|
const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
|
|
|
|
{
|
|
Colour colourGuard(colour);
|
|
stream << " with " << pluralise(N, "message") << ':';
|
|
}
|
|
|
|
while (itMessage != itEnd) {
|
|
// If this assertion is a warning ignore any INFO messages
|
|
if (printInfoMessages || itMessage->type != ResultWas::Info) {
|
|
printMessage();
|
|
if (itMessage != itEnd) {
|
|
Colour colourGuard(dimColour());
|
|
stream << " and";
|
|
}
|
|
continue;
|
|
}
|
|
++itMessage;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
double dur = _sectionStats.durationInSeconds;
|
|
if ( shouldShowDuration( *m_config, dur ) ) {
|
|
stream << getFormattedDuration( dur ) << " 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
|