/* * Created by Martin Moene on 2013-12-05. * Copyright 2012 Martin Moene. 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_TAP_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED #include "catch_reporter_bases.hpp" #include #include "../internal/catch_reporter_registrars.hpp" #include "../internal/catch_console_colour.hpp" namespace Catch { struct TAPReporter : StreamingReporterBase { TAPReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ) {} virtual ~TAPReporter(); static std::string getDescription() { return "Reports test results in TAP format, suitable for test harneses"; } virtual ReporterPreferences getPreferences() const { ReporterPreferences prefs; prefs.shouldRedirectStdOut = false; return prefs; } virtual void noMatchingTestCases( std::string const& spec ) { stream << "# No test cases matched '" << spec << "'" << std::endl; } virtual void assertionStarting( AssertionInfo const& ) { } virtual bool 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, ++counter ); printer.print(); stream << " # " << currentTestCaseInfo->name ; stream << std::endl; return true; } virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); stream << "\n" << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } private: size_t counter = 0; class AssertionPrinter { void operator= ( AssertionPrinter const& ); public: AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages, size_t counter = 0 ) : stream( _stream ) , stats( _stats ) , result( _stats.assertionResult ) , messages( _stats.infoMessages ) , itMessage( _stats.infoMessages.begin() ) , printInfoMessages( _printInfoMessages ) , counter(counter) {} 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( " # TODO" ) ); //else printResultType( Colour::Error, failedString() ); printOriginalExpression(); //printReconstructedExpression(); if( result.isOk() ) printIssue( " # TODO" ); 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: // Colour::LightGrey static Colour::Code dimColour() { return Colour::FileName; } static const char* failedString() { return "not ok"; } static const char* passedString() { return "ok"; } void printSourceInfo() const { Colour colourGuard( Colour::FileName ); stream << result.getSourceInfo() << ":"; } void printResultType( Colour::Code colour, std::string passOrFail ) const { if( !passOrFail.empty() ) { { //Colour colourGuard( colour ); stream << passOrFail << " " << counter; } stream << " -"; } } void printIssue( std::string 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: "; } std::string expr = result.getExpandedExpression(); std::replace( expr.begin(), expr.end(), '\n', ' '); stream << expr; } } 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 compilation error: std::vector::const_iterator itEnd = messages.end(); const std::size_t N = static_cast( 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; AssertionStats const& stats; AssertionResult const& result; std::vector messages; std::vector::const_iterator itMessage; bool printInfoMessages; size_t counter; }; // 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. std::string bothOrAll( std::size_t count ) const { return count == 1 ? "" : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { if( totals.testCases.total() == 0 ) { stream << "1..0 # Skipped: No tests ran."; //stream << "No tests ran."; } else { stream << "1.." << counter; } //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 ) : ""; // stream << // "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 ) { // stream << // "Passed " << bothOrAll( totals.testCases.total() ) // << pluralise( totals.testCases.total(), "test case" ) // << " (no assertions)."; //} //else if( totals.assertions.failed ) { // Colour colour( Colour::ResultError ); // stream << // "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " // "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; //} //else { // Colour colour( Colour::ResultSuccess ); // stream << // "Passed " << bothOrAll( totals.testCases.passed ) // << pluralise( totals.testCases.passed, "test case" ) << // " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; //} } }; INTERNAL_CATCH_REGISTER_REPORTER( "tap", TAPReporter ) } // end namespace Catch #endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED