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 TAPReporterCatch::TAPReporter26 TAPReporter( ReporterConfig const& config ): 27 StreamingReporterBase( config ) { 28 m_reporterPrefs.shouldReportAllAssertions = true; 29 } 30 31 ~TAPReporter() override; 32 getDescriptionCatch::TAPReporter33 static std::string getDescription() { 34 return "Reports test results in TAP format, suitable for test harnesses"; 35 } 36 noMatchingTestCasesCatch::TAPReporter37 void noMatchingTestCases( std::string const& spec ) override { 38 stream << "# No test cases matched '" << spec << "'" << std::endl; 39 } 40 assertionStartingCatch::TAPReporter41 void assertionStarting( AssertionInfo const& ) override {} 42 assertionEndedCatch::TAPReporter43 bool assertionEnded( AssertionStats const& _assertionStats ) override { 44 ++counter; 45 46 stream << "# " << currentTestCaseInfo->name << std::endl; 47 AssertionPrinter printer( stream, _assertionStats, counter ); 48 printer.print(); 49 50 stream << std::endl; 51 return true; 52 } 53 testRunEndedCatch::TAPReporter54 void testRunEnded( TestRunStats const& _testRunStats ) override { 55 printTotals( _testRunStats.totals ); 56 stream << "\n" << std::endl; 57 StreamingReporterBase::testRunEnded( _testRunStats ); 58 } 59 60 private: 61 std::size_t counter = 0; 62 class AssertionPrinter { 63 public: 64 AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; 65 AssertionPrinter( AssertionPrinter const& ) = delete; AssertionPrinter(std::ostream & _stream,AssertionStats const & _stats,std::size_t _counter)66 AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter ) 67 : stream( _stream ) 68 , result( _stats.assertionResult ) 69 , messages( _stats.infoMessages ) 70 , itMessage( _stats.infoMessages.begin() ) 71 , printInfoMessages( true ) 72 , counter(_counter) 73 {} 74 print()75 void print() { 76 itMessage = messages.begin(); 77 78 switch( result.getResultType() ) { 79 case ResultWas::Ok: 80 printResultType( passedString() ); 81 printOriginalExpression(); 82 printReconstructedExpression(); 83 if ( ! result.hasExpression() ) 84 printRemainingMessages( Colour::None ); 85 else 86 printRemainingMessages(); 87 break; 88 case ResultWas::ExpressionFailed: 89 if (result.isOk()) { 90 printResultType(passedString()); 91 } else { 92 printResultType(failedString()); 93 } 94 printOriginalExpression(); 95 printReconstructedExpression(); 96 if (result.isOk()) { 97 printIssue(" # TODO"); 98 } 99 printRemainingMessages(); 100 break; 101 case ResultWas::ThrewException: 102 printResultType( failedString() ); 103 printIssue( "unexpected exception with message:" ); 104 printMessage(); 105 printExpressionWas(); 106 printRemainingMessages(); 107 break; 108 case ResultWas::FatalErrorCondition: 109 printResultType( failedString() ); 110 printIssue( "fatal error condition with message:" ); 111 printMessage(); 112 printExpressionWas(); 113 printRemainingMessages(); 114 break; 115 case ResultWas::DidntThrowException: 116 printResultType( failedString() ); 117 printIssue( "expected exception, got none" ); 118 printExpressionWas(); 119 printRemainingMessages(); 120 break; 121 case ResultWas::Info: 122 printResultType( "info" ); 123 printMessage(); 124 printRemainingMessages(); 125 break; 126 case ResultWas::Warning: 127 printResultType( "warning" ); 128 printMessage(); 129 printRemainingMessages(); 130 break; 131 case ResultWas::ExplicitFailure: 132 printResultType( failedString() ); 133 printIssue( "explicitly" ); 134 printRemainingMessages( Colour::None ); 135 break; 136 // These cases are here to prevent compiler warnings 137 case ResultWas::Unknown: 138 case ResultWas::FailureBit: 139 case ResultWas::Exception: 140 printResultType( "** internal error **" ); 141 break; 142 } 143 } 144 145 private: dimColour()146 static Colour::Code dimColour() { return Colour::FileName; } 147 failedString()148 static const char* failedString() { return "not ok"; } passedString()149 static const char* passedString() { return "ok"; } 150 printSourceInfo() const151 void printSourceInfo() const { 152 Colour colourGuard( dimColour() ); 153 stream << result.getSourceInfo() << ":"; 154 } 155 printResultType(std::string const & passOrFail) const156 void printResultType( std::string const& passOrFail ) const { 157 if( !passOrFail.empty() ) { 158 stream << passOrFail << ' ' << counter << " -"; 159 } 160 } 161 printIssue(std::string const & issue) const162 void printIssue( std::string const& issue ) const { 163 stream << " " << issue; 164 } 165 printExpressionWas()166 void printExpressionWas() { 167 if( result.hasExpression() ) { 168 stream << ";"; 169 { 170 Colour colour( dimColour() ); 171 stream << " expression was:"; 172 } 173 printOriginalExpression(); 174 } 175 } 176 printOriginalExpression() const177 void printOriginalExpression() const { 178 if( result.hasExpression() ) { 179 stream << " " << result.getExpression(); 180 } 181 } 182 printReconstructedExpression() const183 void printReconstructedExpression() const { 184 if( result.hasExpandedExpression() ) { 185 { 186 Colour colour( dimColour() ); 187 stream << " for: "; 188 } 189 std::string expr = result.getExpandedExpression(); 190 std::replace( expr.begin(), expr.end(), '\n', ' '); 191 stream << expr; 192 } 193 } 194 printMessage()195 void printMessage() { 196 if ( itMessage != messages.end() ) { 197 stream << " '" << itMessage->message << "'"; 198 ++itMessage; 199 } 200 } 201 printRemainingMessages(Colour::Code colour=dimColour ())202 void printRemainingMessages( Colour::Code colour = dimColour() ) { 203 if (itMessage == messages.end()) { 204 return; 205 } 206 207 const auto itEnd = messages.cend(); 208 const auto 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 while( 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 continue; 224 } 225 ++itMessage; 226 } 227 } 228 229 private: 230 std::ostream& stream; 231 AssertionResult const& result; 232 std::vector<MessageInfo> messages; 233 std::vector<MessageInfo>::const_iterator itMessage; 234 bool printInfoMessages; 235 std::size_t counter; 236 }; 237 printTotalsCatch::TAPReporter238 void printTotals( const Totals& totals ) const { 239 stream << "1.." << totals.assertions.total(); 240 if( totals.testCases.total() == 0 ) { 241 stream << " # Skipped: No tests ran."; 242 } 243 } 244 }; 245 246 #ifdef CATCH_IMPL ~TAPReporter()247 TAPReporter::~TAPReporter() {} 248 #endif 249 250 CATCH_REGISTER_REPORTER( "tap", TAPReporter ) 251 252 } // end namespace Catch 253 254 #endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 255