Blame single_include/catch_reporter_tap.hpp

rpm-build a7f80b
/*
rpm-build a7f80b
 *  Created by Colton Wolkins on 2015-08-15.
rpm-build a7f80b
 *  Copyright 2015 Martin Moene. All rights reserved.
rpm-build a7f80b
 *
rpm-build a7f80b
 *  Distributed under the Boost Software License, Version 1.0. (See accompanying
rpm-build a7f80b
 *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
rpm-build a7f80b
 */
rpm-build a7f80b
#ifndef TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED
rpm-build a7f80b
#define TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED
rpm-build a7f80b
rpm-build a7f80b
rpm-build a7f80b
// Don't #include any Catch headers here - we can assume they are already
rpm-build a7f80b
// included before this header.
rpm-build a7f80b
// This is not good practice in general but is necessary in this case so this
rpm-build a7f80b
// file can be distributed as a single header that works with the main
rpm-build a7f80b
// Catch single header.
rpm-build a7f80b
rpm-build a7f80b
#include <algorithm>
rpm-build a7f80b
rpm-build a7f80b
namespace Catch {
rpm-build a7f80b
rpm-build a7f80b
    struct TAPReporter : StreamingReporterBase<TAPReporter> {
rpm-build a7f80b
rpm-build a7f80b
        using StreamingReporterBase::StreamingReporterBase;
rpm-build a7f80b
rpm-build a7f80b
        ~TAPReporter() override;
rpm-build a7f80b
rpm-build a7f80b
        static std::string getDescription() {
rpm-build a7f80b
            return "Reports test results in TAP format, suitable for test harnesses";
rpm-build a7f80b
        }
rpm-build a7f80b
rpm-build a7f80b
        ReporterPreferences getPreferences() const override {
rpm-build a7f80b
            ReporterPreferences prefs;
rpm-build a7f80b
            prefs.shouldRedirectStdOut = false;
rpm-build a7f80b
            return prefs;
rpm-build a7f80b
        }
rpm-build a7f80b
rpm-build a7f80b
        void noMatchingTestCases( std::string const& spec ) override {
rpm-build a7f80b
            stream << "# No test cases matched '" << spec << "'" << std::endl;
rpm-build a7f80b
        }
rpm-build a7f80b
rpm-build a7f80b
        void assertionStarting( AssertionInfo const& ) override {}
rpm-build a7f80b
rpm-build a7f80b
        bool assertionEnded( AssertionStats const& _assertionStats ) override {
rpm-build a7f80b
            ++counter;
rpm-build a7f80b
rpm-build a7f80b
            AssertionPrinter printer( stream, _assertionStats, counter );
rpm-build a7f80b
            printer.print();
rpm-build a7f80b
            stream << " # " << currentTestCaseInfo->name ;
rpm-build a7f80b
rpm-build a7f80b
            stream << std::endl;
rpm-build a7f80b
            return true;
rpm-build a7f80b
        }
rpm-build a7f80b
rpm-build a7f80b
        void testRunEnded( TestRunStats const& _testRunStats ) override {
rpm-build a7f80b
            printTotals( _testRunStats.totals );
rpm-build a7f80b
            stream << "\n" << std::endl;
rpm-build a7f80b
            StreamingReporterBase::testRunEnded( _testRunStats );
rpm-build a7f80b
        }
rpm-build a7f80b
rpm-build a7f80b
    private:
rpm-build a7f80b
        std::size_t counter = 0;
rpm-build a7f80b
        class AssertionPrinter {
rpm-build a7f80b
        public:
rpm-build a7f80b
            AssertionPrinter& operator= ( AssertionPrinter const& ) = delete;
rpm-build a7f80b
            AssertionPrinter( AssertionPrinter const& ) = delete;
rpm-build a7f80b
            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter )
rpm-build a7f80b
            : stream( _stream )
rpm-build a7f80b
            , result( _stats.assertionResult )
rpm-build a7f80b
            , messages( _stats.infoMessages )
rpm-build a7f80b
            , itMessage( _stats.infoMessages.begin() )
rpm-build a7f80b
            , printInfoMessages( true )
rpm-build a7f80b
            , counter(_counter)
rpm-build a7f80b
            {}
rpm-build a7f80b
rpm-build a7f80b
            void print() {
rpm-build a7f80b
                itMessage = messages.begin();
rpm-build a7f80b
rpm-build a7f80b
                switch( result.getResultType() ) {
rpm-build a7f80b
                    case ResultWas::Ok:
rpm-build a7f80b
                        printResultType( passedString() );
rpm-build a7f80b
                        printOriginalExpression();
rpm-build a7f80b
                        printReconstructedExpression();
rpm-build a7f80b
                        if ( ! result.hasExpression() )
rpm-build a7f80b
                            printRemainingMessages( Colour::None );
rpm-build a7f80b
                        else
rpm-build a7f80b
                            printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::ExpressionFailed:
rpm-build a7f80b
                        if (result.isOk()) {
rpm-build a7f80b
                            printResultType(passedString());
rpm-build a7f80b
                        } else {
rpm-build a7f80b
                            printResultType(failedString());
rpm-build a7f80b
                        }
rpm-build a7f80b
                        printOriginalExpression();
rpm-build a7f80b
                        printReconstructedExpression();
rpm-build a7f80b
                        if (result.isOk()) {
rpm-build a7f80b
                            printIssue(" # TODO");
rpm-build a7f80b
                        }
rpm-build a7f80b
                        printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::ThrewException:
rpm-build a7f80b
                        printResultType( failedString() );
rpm-build a7f80b
                        printIssue( "unexpected exception with message:" );
rpm-build a7f80b
                        printMessage();
rpm-build a7f80b
                        printExpressionWas();
rpm-build a7f80b
                        printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::FatalErrorCondition:
rpm-build a7f80b
                        printResultType( failedString() );
rpm-build a7f80b
                        printIssue( "fatal error condition with message:" );
rpm-build a7f80b
                        printMessage();
rpm-build a7f80b
                        printExpressionWas();
rpm-build a7f80b
                        printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::DidntThrowException:
rpm-build a7f80b
                        printResultType( failedString() );
rpm-build a7f80b
                        printIssue( "expected exception, got none" );
rpm-build a7f80b
                        printExpressionWas();
rpm-build a7f80b
                        printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::Info:
rpm-build a7f80b
                        printResultType( "info" );
rpm-build a7f80b
                        printMessage();
rpm-build a7f80b
                        printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::Warning:
rpm-build a7f80b
                        printResultType( "warning" );
rpm-build a7f80b
                        printMessage();
rpm-build a7f80b
                        printRemainingMessages();
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    case ResultWas::ExplicitFailure:
rpm-build a7f80b
                        printResultType( failedString() );
rpm-build a7f80b
                        printIssue( "explicitly" );
rpm-build a7f80b
                        printRemainingMessages( Colour::None );
rpm-build a7f80b
                        break;
rpm-build a7f80b
                    // These cases are here to prevent compiler warnings
rpm-build a7f80b
                    case ResultWas::Unknown:
rpm-build a7f80b
                    case ResultWas::FailureBit:
rpm-build a7f80b
                    case ResultWas::Exception:
rpm-build a7f80b
                        printResultType( "** internal error **" );
rpm-build a7f80b
                        break;
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
        private:
rpm-build a7f80b
            static Colour::Code dimColour() { return Colour::FileName; }
rpm-build a7f80b
rpm-build a7f80b
            static const char* failedString() { return "not ok"; }
rpm-build a7f80b
            static const char* passedString() { return "ok"; }
rpm-build a7f80b
rpm-build a7f80b
            void printSourceInfo() const {
rpm-build a7f80b
                Colour colourGuard( dimColour() );
rpm-build a7f80b
                stream << result.getSourceInfo() << ":";
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printResultType( std::string const& passOrFail ) const {
rpm-build a7f80b
                if( !passOrFail.empty() ) {
rpm-build a7f80b
                    stream << passOrFail << ' ' << counter << " -";
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printIssue( std::string const& issue ) const {
rpm-build a7f80b
                stream << " " << issue;
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printExpressionWas() {
rpm-build a7f80b
                if( result.hasExpression() ) {
rpm-build a7f80b
                    stream << ";";
rpm-build a7f80b
                    {
rpm-build a7f80b
                        Colour colour( dimColour() );
rpm-build a7f80b
                        stream << " expression was:";
rpm-build a7f80b
                    }
rpm-build a7f80b
                    printOriginalExpression();
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printOriginalExpression() const {
rpm-build a7f80b
                if( result.hasExpression() ) {
rpm-build a7f80b
                    stream << " " << result.getExpression();
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printReconstructedExpression() const {
rpm-build a7f80b
                if( result.hasExpandedExpression() ) {
rpm-build a7f80b
                    {
rpm-build a7f80b
                        Colour colour( dimColour() );
rpm-build a7f80b
                        stream << " for: ";
rpm-build a7f80b
                    }
rpm-build a7f80b
                    std::string expr = result.getExpandedExpression();
rpm-build a7f80b
                    std::replace( expr.begin(), expr.end(), '\n', ' ');
rpm-build a7f80b
                    stream << expr;
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printMessage() {
rpm-build a7f80b
                if ( itMessage != messages.end() ) {
rpm-build a7f80b
                    stream << " '" << itMessage->message << "'";
rpm-build a7f80b
                    ++itMessage;
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
            void printRemainingMessages( Colour::Code colour = dimColour() ) {
rpm-build a7f80b
                if (itMessage == messages.end()) {
rpm-build a7f80b
                    return;
rpm-build a7f80b
                }
rpm-build a7f80b
rpm-build a7f80b
                // using messages.end() directly (or auto) yields compilation error:
rpm-build a7f80b
                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
rpm-build a7f80b
                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
rpm-build a7f80b
rpm-build a7f80b
                {
rpm-build a7f80b
                    Colour colourGuard( colour );
rpm-build a7f80b
                    stream << " with " << pluralise( N, "message" ) << ":";
rpm-build a7f80b
                }
rpm-build a7f80b
rpm-build a7f80b
                for(; itMessage != itEnd; ) {
rpm-build a7f80b
                    // If this assertion is a warning ignore any INFO messages
rpm-build a7f80b
                    if( printInfoMessages || itMessage->type != ResultWas::Info ) {
rpm-build a7f80b
                        stream << " '" << itMessage->message << "'";
rpm-build a7f80b
                        if ( ++itMessage != itEnd ) {
rpm-build a7f80b
                            Colour colourGuard( dimColour() );
rpm-build a7f80b
                            stream << " and";
rpm-build a7f80b
                        }
rpm-build a7f80b
                    }
rpm-build a7f80b
                }
rpm-build a7f80b
            }
rpm-build a7f80b
rpm-build a7f80b
        private:
rpm-build a7f80b
            std::ostream& stream;
rpm-build a7f80b
            AssertionResult const& result;
rpm-build a7f80b
            std::vector<MessageInfo> messages;
rpm-build a7f80b
            std::vector<MessageInfo>::const_iterator itMessage;
rpm-build a7f80b
            bool printInfoMessages;
rpm-build a7f80b
            std::size_t counter;
rpm-build a7f80b
        };
rpm-build a7f80b
rpm-build a7f80b
        void printTotals( const Totals& totals ) const {
rpm-build a7f80b
            if( totals.testCases.total() == 0 ) {
rpm-build a7f80b
                stream << "1..0 # Skipped: No tests ran.";
rpm-build a7f80b
            } else {
rpm-build a7f80b
                stream << "1.." << counter;
rpm-build a7f80b
            }
rpm-build a7f80b
        }
rpm-build a7f80b
    };
rpm-build a7f80b
rpm-build a7f80b
#ifdef CATCH_IMPL
rpm-build a7f80b
    TAPReporter::~TAPReporter() {}
rpm-build a7f80b
#endif
rpm-build a7f80b
rpm-build a7f80b
    CATCH_REGISTER_REPORTER( "tap", TAPReporter )
rpm-build a7f80b
rpm-build a7f80b
} // end namespace Catch
rpm-build a7f80b
rpm-build a7f80b
#endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED