1 /* 2 * Created by Colton Wolkins on 2015-08-15. 3 * Copyright 2015 Martin Moene. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 #ifndef TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 10 11 12 // Don't #include any Catch headers here - we can assume they are already 13 // included before this header. 14 // This is not good practice in general but is necessary in this case so this 15 // file can be distributed as a single header that works with the main 16 // Catch single header. 17 18 #include <algorithm> 19 20 namespace Catch { 21 22 struct TAPReporter : StreamingReporterBase<TAPReporter> { 23 24 using StreamingReporterBase::StreamingReporterBase; 25 26 ~TAPReporter() override; 27 getDescriptionCatch::TAPReporter28 static std::string getDescription() { 29 return "Reports test results in TAP format, suitable for test harnesses"; 30 } 31 getPreferencesCatch::TAPReporter32 ReporterPreferences getPreferences() const override { 33 return m_reporterPrefs; 34 } 35 noMatchingTestCasesCatch::TAPReporter36 void noMatchingTestCases( std::string const& spec ) override { 37 stream << "# No test cases matched '" << spec << "'" << std::endl; 38 } 39 assertionStartingCatch::TAPReporter40 void assertionStarting( AssertionInfo const& ) override {} 41 assertionEndedCatch::TAPReporter42 bool assertionEnded( AssertionStats const& _assertionStats ) override { 43 ++counter; 44 45 stream << "# " << currentTestCaseInfo->name << std::endl; 46 AssertionPrinter printer( stream, _assertionStats, counter ); 47 printer.print(); 48 49 stream << std::endl; 50 return true; 51 } 52 testRunEndedCatch::TAPReporter53 void testRunEnded( TestRunStats const& _testRunStats ) override { 54 printTotals( _testRunStats.totals ); 55 stream << "\n" << std::endl; 56 StreamingReporterBase::testRunEnded( _testRunStats ); 57 } 58 59 private: 60 std::size_t counter = 0; 61 class AssertionPrinter { 62 public: 63 AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; 64 AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter(std::ostream & _stream,AssertionStats const & _stats,std::size_t _counter)65 AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter ) 66 : stream( _stream ) 67 , result( _stats.assertionResult ) 68 , messages( _stats.infoMessages ) 69 , itMessage( _stats.infoMessages.begin() ) 70 , printInfoMessages( true ) 71 , counter(_counter) 72 {} 73 print()74 void print() { 75 itMessage = messages.begin(); 76 77 switch( result.getResultType() ) { 78 case ResultWas::Ok: 79 printResultType( passedString() ); 80 printOriginalExpression(); 81 printReconstructedExpression(); 82 if ( ! result.hasExpression() ) 83 printRemainingMessages( Colour::None ); 84 else 85 printRemainingMessages(); 86 break; 87 case ResultWas::ExpressionFailed: 88 if (result.isOk()) { 89 printResultType(passedString()); 90 } else { 91 printResultType(failedString()); 92 } 93 printOriginalExpression(); 94 printReconstructedExpression(); 95 if (result.isOk()) { 96 printIssue(" # TODO"); 97 } 98 printRemainingMessages(); 99 break; 100 case ResultWas::ThrewException: 101 printResultType( failedString() ); 102 printIssue( "unexpected exception with message:" ); 103 printMessage(); 104 printExpressionWas(); 105 printRemainingMessages(); 106 break; 107 case ResultWas::FatalErrorCondition: 108 printResultType( failedString() ); 109 printIssue( "fatal error condition with message:" ); 110 printMessage(); 111 printExpressionWas(); 112 printRemainingMessages(); 113 break; 114 case ResultWas::DidntThrowException: 115 printResultType( failedString() ); 116 printIssue( "expected exception, got none" ); 117 printExpressionWas(); 118 printRemainingMessages(); 119 break; 120 case ResultWas::Info: 121 printResultType( "info" ); 122 printMessage(); 123 printRemainingMessages(); 124 break; 125 case ResultWas::Warning: 126 printResultType( "warning" ); 127 printMessage(); 128 printRemainingMessages(); 129 break; 130 case ResultWas::ExplicitFailure: 131 printResultType( failedString() ); 132 printIssue( "explicitly" ); 133 printRemainingMessages( Colour::None ); 134 break; 135 // These cases are here to prevent compiler warnings 136 case ResultWas::Unknown: 137 case ResultWas::FailureBit: 138 case ResultWas::Exception: 139 printResultType( "** internal error **" ); 140 break; 141 } 142 } 143 144 private: dimColour()145 static Colour::Code dimColour() { return Colour::FileName; } 146 failedString()147 static const char* failedString() { return "not ok"; } passedString()148 static const char* passedString() { return "ok"; } 149 printSourceInfo() const150 void printSourceInfo() const { 151 Colour colourGuard( dimColour() ); 152 stream << result.getSourceInfo() << ":"; 153 } 154 printResultType(std::string const & passOrFail) const155 void printResultType( std::string const& passOrFail ) const { 156 if( !passOrFail.empty() ) { 157 stream << passOrFail << ' ' << counter << " -"; 158 } 159 } 160 printIssue(std::string const & issue) const161 void printIssue( std::string const& issue ) const { 162 stream << " " << issue; 163 } 164 printExpressionWas()165 void printExpressionWas() { 166 if( result.hasExpression() ) { 167 stream << ";"; 168 { 169 Colour colour( dimColour() ); 170 stream << " expression was:"; 171 } 172 printOriginalExpression(); 173 } 174 } 175 printOriginalExpression() const176 void printOriginalExpression() const { 177 if( result.hasExpression() ) { 178 stream << " " << result.getExpression(); 179 } 180 } 181 printReconstructedExpression() const182 void printReconstructedExpression() const { 183 if( result.hasExpandedExpression() ) { 184 { 185 Colour colour( dimColour() ); 186 stream << " for: "; 187 } 188 std::string expr = result.getExpandedExpression(); 189 std::replace( expr.begin(), expr.end(), '\n', ' '); 190 stream << expr; 191 } 192 } 193 printMessage()194 void printMessage() { 195 if ( itMessage != messages.end() ) { 196 stream << " '" << itMessage->message << "'"; 197 ++itMessage; 198 } 199 } 200 printRemainingMessages(Colour::Code colour=dimColour ())201 void printRemainingMessages( Colour::Code colour = dimColour() ) { 202 if (itMessage == messages.end()) { 203 return; 204 } 205 206 // using messages.end() directly (or auto) yields compilation error: 207 std::vector<MessageInfo>::const_iterator itEnd = messages.end(); 208 const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) ); 209 210 { 211 Colour colourGuard( colour ); 212 stream << " with " << pluralise( N, "message" ) << ":"; 213 } 214 215 for(; itMessage != itEnd; ) { 216 // If this assertion is a warning ignore any INFO messages 217 if( printInfoMessages || itMessage->type != ResultWas::Info ) { 218 stream << " '" << itMessage->message << "'"; 219 if ( ++itMessage != itEnd ) { 220 Colour colourGuard( dimColour() ); 221 stream << " and"; 222 } 223 } 224 } 225 } 226 227 private: 228 std::ostream& stream; 229 AssertionResult const& result; 230 std::vector<MessageInfo> messages; 231 std::vector<MessageInfo>::const_iterator itMessage; 232 bool printInfoMessages; 233 std::size_t counter; 234 }; 235 printTotalsCatch::TAPReporter236 void printTotals( const Totals& totals ) const { 237 if( totals.testCases.total() == 0 ) { 238 stream << "1..0 # Skipped: No tests ran."; 239 } else { 240 stream << "1.." << counter; 241 } 242 } 243 }; 244 245 #ifdef CATCH_IMPL ~TAPReporter()246 TAPReporter::~TAPReporter() {} 247 #endif 248 249 CATCH_REGISTER_REPORTER( "tap", TAPReporter ) 250 251 } // end namespace Catch 252 253 #endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 254