catch_reporter_tap.hpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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. // Don't #include any Catch headers here - we can assume they are already
  11. // included before this header.
  12. // This is not good practice in general but is necessary in this case so this
  13. // file can be distributed as a single header that works with the main
  14. // Catch single header.
  15. #include <algorithm>
  16. namespace Catch {
  17. struct TAPReporter : StreamingReporterBase<TAPReporter> {
  18. using StreamingReporterBase::StreamingReporterBase;
  19. TAPReporter( ReporterConfig const& config ):
  20. StreamingReporterBase( config ) {
  21. m_reporterPrefs.shouldReportAllAssertions = true;
  22. }
  23. ~TAPReporter() override;
  24. static std::string getDescription() {
  25. return "Reports test results in TAP format, suitable for test harnesses";
  26. }
  27. void noMatchingTestCases( std::string const& spec ) override {
  28. stream << "# No test cases matched '" << spec << "'" << std::endl;
  29. }
  30. void assertionStarting( AssertionInfo const& ) override {}
  31. bool assertionEnded( AssertionStats const& _assertionStats ) override {
  32. ++counter;
  33. stream << "# " << currentTestCaseInfo->name << std::endl;
  34. AssertionPrinter printer( stream, _assertionStats, counter );
  35. printer.print();
  36. stream << std::endl;
  37. return true;
  38. }
  39. void testRunEnded( TestRunStats const& _testRunStats ) override {
  40. printTotals( _testRunStats.totals );
  41. stream << "\n" << std::endl;
  42. StreamingReporterBase::testRunEnded( _testRunStats );
  43. }
  44. private:
  45. std::size_t counter = 0;
  46. class AssertionPrinter {
  47. public:
  48. AssertionPrinter& operator= ( AssertionPrinter const& ) = delete;
  49. AssertionPrinter( AssertionPrinter const& ) = delete;
  50. AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter )
  51. : stream( _stream )
  52. , result( _stats.assertionResult )
  53. , messages( _stats.infoMessages )
  54. , itMessage( _stats.infoMessages.begin() )
  55. , printInfoMessages( true )
  56. , counter(_counter)
  57. {}
  58. void print() {
  59. itMessage = messages.begin();
  60. switch( result.getResultType() ) {
  61. case ResultWas::Ok:
  62. printResultType( passedString() );
  63. printOriginalExpression();
  64. printReconstructedExpression();
  65. if ( ! result.hasExpression() )
  66. printRemainingMessages( Colour::None );
  67. else
  68. printRemainingMessages();
  69. break;
  70. case ResultWas::ExpressionFailed:
  71. if (result.isOk()) {
  72. printResultType(passedString());
  73. } else {
  74. printResultType(failedString());
  75. }
  76. printOriginalExpression();
  77. printReconstructedExpression();
  78. if (result.isOk()) {
  79. printIssue(" # TODO");
  80. }
  81. printRemainingMessages();
  82. break;
  83. case ResultWas::ThrewException:
  84. printResultType( failedString() );
  85. printIssue( "unexpected exception with message:" );
  86. printMessage();
  87. printExpressionWas();
  88. printRemainingMessages();
  89. break;
  90. case ResultWas::FatalErrorCondition:
  91. printResultType( failedString() );
  92. printIssue( "fatal error condition with message:" );
  93. printMessage();
  94. printExpressionWas();
  95. printRemainingMessages();
  96. break;
  97. case ResultWas::DidntThrowException:
  98. printResultType( failedString() );
  99. printIssue( "expected exception, got none" );
  100. printExpressionWas();
  101. printRemainingMessages();
  102. break;
  103. case ResultWas::Info:
  104. printResultType( "info" );
  105. printMessage();
  106. printRemainingMessages();
  107. break;
  108. case ResultWas::Warning:
  109. printResultType( "warning" );
  110. printMessage();
  111. printRemainingMessages();
  112. break;
  113. case ResultWas::ExplicitFailure:
  114. printResultType( failedString() );
  115. printIssue( "explicitly" );
  116. printRemainingMessages( Colour::None );
  117. break;
  118. // These cases are here to prevent compiler warnings
  119. case ResultWas::Unknown:
  120. case ResultWas::FailureBit:
  121. case ResultWas::Exception:
  122. printResultType( "** internal error **" );
  123. break;
  124. }
  125. }
  126. private:
  127. static Colour::Code dimColour() { return Colour::FileName; }
  128. static const char* failedString() { return "not ok"; }
  129. static const char* passedString() { return "ok"; }
  130. void printSourceInfo() const {
  131. Colour colourGuard( dimColour() );
  132. stream << result.getSourceInfo() << ":";
  133. }
  134. void printResultType( std::string const& passOrFail ) const {
  135. if( !passOrFail.empty() ) {
  136. stream << passOrFail << ' ' << counter << " -";
  137. }
  138. }
  139. void printIssue( std::string const& issue ) const {
  140. stream << " " << issue;
  141. }
  142. void printExpressionWas() {
  143. if( result.hasExpression() ) {
  144. stream << ";";
  145. {
  146. Colour colour( dimColour() );
  147. stream << " expression was:";
  148. }
  149. printOriginalExpression();
  150. }
  151. }
  152. void printOriginalExpression() const {
  153. if( result.hasExpression() ) {
  154. stream << " " << result.getExpression();
  155. }
  156. }
  157. void printReconstructedExpression() const {
  158. if( result.hasExpandedExpression() ) {
  159. {
  160. Colour colour( dimColour() );
  161. stream << " for: ";
  162. }
  163. std::string expr = result.getExpandedExpression();
  164. std::replace( expr.begin(), expr.end(), '\n', ' ');
  165. stream << expr;
  166. }
  167. }
  168. void printMessage() {
  169. if ( itMessage != messages.end() ) {
  170. stream << " '" << itMessage->message << "'";
  171. ++itMessage;
  172. }
  173. }
  174. void printRemainingMessages( Colour::Code colour = dimColour() ) {
  175. if (itMessage == messages.end()) {
  176. return;
  177. }
  178. const auto itEnd = messages.cend();
  179. const auto N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
  180. {
  181. Colour colourGuard( colour );
  182. stream << " with " << pluralise( N, "message" ) << ":";
  183. }
  184. while( itMessage != itEnd ) {
  185. // If this assertion is a warning ignore any INFO messages
  186. if( printInfoMessages || itMessage->type != ResultWas::Info ) {
  187. stream << " '" << itMessage->message << "'";
  188. if ( ++itMessage != itEnd ) {
  189. Colour colourGuard( dimColour() );
  190. stream << " and";
  191. }
  192. continue;
  193. }
  194. ++itMessage;
  195. }
  196. }
  197. private:
  198. std::ostream& stream;
  199. AssertionResult const& result;
  200. std::vector<MessageInfo> messages;
  201. std::vector<MessageInfo>::const_iterator itMessage;
  202. bool printInfoMessages;
  203. std::size_t counter;
  204. };
  205. void printTotals( const Totals& totals ) const {
  206. stream << "1.." << totals.assertions.total();
  207. if( totals.testCases.total() == 0 ) {
  208. stream << " # Skipped: No tests ran.";
  209. }
  210. }
  211. };
  212. #ifdef CATCH_IMPL
  213. TAPReporter::~TAPReporter() {}
  214. #endif
  215. CATCH_REGISTER_REPORTER( "tap", TAPReporter )
  216. } // end namespace Catch
  217. #endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED