/* * 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::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 messages; std::vector::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