Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/single_include/catch_reporter_tap.hpp
diff options
context:
space:
mode:
authorMartin Hořeňovský2018-01-10 13:53:04 +0100
committerMartin Hořeňovský2018-01-10 13:53:04 +0100
commitcd76f5730c9a3afa19f3b9c83608d9c7ab325a19 (patch)
treeb189f1e5399737491266204879bbd185669f799a /single_include/catch_reporter_tap.hpp
parentf5910f38ef38ac2cca9f3830804abf6d9a8d40bf (diff)
downloadcatch3-2.1.0.tar.gz
v2.1.0v2.1.0
Diffstat (limited to 'single_include/catch_reporter_tap.hpp')
-rw-r--r--single_include/catch_reporter_tap.hpp255
1 files changed, 255 insertions, 0 deletions
diff --git a/single_include/catch_reporter_tap.hpp b/single_include/catch_reporter_tap.hpp
new file mode 100644
index 00000000..edb75b5a
--- /dev/null
+++ b/single_include/catch_reporter_tap.hpp
@@ -0,0 +1,255 @@
+/*
+ * Created by Colton Wolkins on 2015-08-15.
+ * Copyright 2015 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
+
+
+// Don't #include any Catch headers here - we can assume they are already
+// included before this header.
+// This is not good practice in general but is necessary in this case so this
+// file can be distributed as a single header that works with the main
+// Catch single header.
+
+#include <algorithm>
+
+namespace Catch {
+
+ struct TAPReporter : StreamingReporterBase<TAPReporter> {
+
+ using StreamingReporterBase::StreamingReporterBase;
+
+ ~TAPReporter() override;
+
+ static std::string getDescription() {
+ return "Reports test results in TAP format, suitable for test harneses";
+ }
+
+ ReporterPreferences getPreferences() const override {
+ ReporterPreferences prefs;
+ prefs.shouldRedirectStdOut = false;
+ return prefs;
+ }
+
+ void noMatchingTestCases( std::string const& spec ) override {
+ stream << "# No test cases matched '" << spec << "'" << std::endl;
+ }
+
+ void assertionStarting( AssertionInfo const& ) override {}
+
+ bool assertionEnded( AssertionStats const& _assertionStats ) override {
+ ++counter;
+
+ AssertionPrinter printer( stream, _assertionStats, counter );
+ printer.print();
+ stream << " # " << currentTestCaseInfo->name ;
+
+ stream << std::endl;
+ return true;
+ }
+
+ void testRunEnded( TestRunStats const& _testRunStats ) override {
+ printTotals( _testRunStats.totals );
+ stream << "\n" << std::endl;
+ StreamingReporterBase::testRunEnded( _testRunStats );
+ }
+
+ private:
+ std::size_t counter = 0;
+ class AssertionPrinter {
+ public:
+ AssertionPrinter& operator= ( AssertionPrinter const& ) = delete;
+ AssertionPrinter( AssertionPrinter const& ) = delete;
+ AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter )
+ : stream( _stream )
+ , result( _stats.assertionResult )
+ , messages( _stats.infoMessages )
+ , itMessage( _stats.infoMessages.begin() )
+ , printInfoMessages( true )
+ , counter(_counter)
+ {}
+
+ void print() {
+ itMessage = messages.begin();
+
+ switch( result.getResultType() ) {
+ case ResultWas::Ok:
+ printResultType( passedString() );
+ printOriginalExpression();
+ printReconstructedExpression();
+ if ( ! result.hasExpression() )
+ printRemainingMessages( Colour::None );
+ else
+ printRemainingMessages();
+ break;
+ case ResultWas::ExpressionFailed:
+ if (result.isOk()) {
+ printResultType(passedString());
+ } else {
+ printResultType(failedString());
+ }
+ printOriginalExpression();
+ printReconstructedExpression();
+ if (result.isOk()) {
+ printIssue(" # TODO");
+ }
+ printRemainingMessages();
+ break;
+ case ResultWas::ThrewException:
+ printResultType( failedString() );
+ printIssue( "unexpected exception with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::FatalErrorCondition:
+ printResultType( failedString() );
+ printIssue( "fatal error condition with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::DidntThrowException:
+ printResultType( failedString() );
+ printIssue( "expected exception, got none" );
+ printExpressionWas();
+ printRemainingMessages();
+ break;
+ case ResultWas::Info:
+ printResultType( "info" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::Warning:
+ printResultType( "warning" );
+ printMessage();
+ printRemainingMessages();
+ break;
+ case ResultWas::ExplicitFailure:
+ printResultType( 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( "** internal error **" );
+ break;
+ }
+ }
+
+ private:
+ 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( dimColour() );
+ stream << result.getSourceInfo() << ":";
+ }
+
+ void printResultType( std::string const& passOrFail ) const {
+ if( !passOrFail.empty() ) {
+ stream << passOrFail << ' ' << counter << " -";
+ }
+ }
+
+ 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: ";
+ }
+ 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 (or auto) yields 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;
+ std::size_t counter;
+ };
+
+ void printTotals( const Totals& totals ) const {
+ if( totals.testCases.total() == 0 ) {
+ stream << "1..0 # Skipped: No tests ran.";
+ } else {
+ stream << "1.." << counter;
+ }
+ }
+ };
+
+#ifdef CATCH_IMPL
+ TAPReporter::~TAPReporter() {}
+#endif
+
+ CATCH_REGISTER_REPORTER( "tap", TAPReporter )
+
+} // end namespace Catch
+
+#endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED