mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	Add TAP reporter
This is a hackish attempt to add a TAP reporter (see philsquared/Catch#309 ) by following the TAP 12 specification <http://testanything.org/tap-specification.html>. I'm unsure how well I did in following the spec or with following good C++ guidelines. Comments are appreciated. Signed-off-by: Colton Wolkins (Ogre) <frostyfrog2@gmail.com>
This commit is contained in:
		 Colton Wolkins (Ogre)
					Colton Wolkins (Ogre)
				
			
				
					committed by
					
						 Martin Hořeňovský
						Martin Hořeňovský
					
				
			
			
				
	
			
			
			 Martin Hořeňovský
						Martin Hořeňovský
					
				
			
						parent
						
							b753f05d74
						
					
				
				
					commit
					a63ce953a0
				
			
							
								
								
									
										309
									
								
								include/reporters/catch_reporter_tap.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								include/reporters/catch_reporter_tap.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | ||||
| /* | ||||
|  *  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 <algorithm> | ||||
|  | ||||
| #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<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; | ||||
|             AssertionStats const& stats; | ||||
|             AssertionResult const& result; | ||||
|             std::vector<MessageInfo> messages; | ||||
|             std::vector<MessageInfo>::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 | ||||
		Reference in New Issue
	
	Block a user