catch_reporter_compact.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * Created by Martin on 2017-11-14.
  3. *
  4. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. */
  7. #include "catch_reporter_compact.h"
  8. #include "../internal/catch_reporter_registrars.hpp"
  9. #include "../internal/catch_console_colour.h"
  10. namespace {
  11. #ifdef CATCH_PLATFORM_MAC
  12. const char* failedString() { return "FAILED"; }
  13. const char* passedString() { return "PASSED"; }
  14. #else
  15. const char* failedString() { return "failed"; }
  16. const char* passedString() { return "passed"; }
  17. #endif
  18. // Colour::LightGrey
  19. Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
  20. std::string bothOrAll( std::size_t count ) {
  21. return count == 1 ? std::string() :
  22. count == 2 ? "both " : "all " ;
  23. }
  24. } // anon namespace
  25. namespace Catch {
  26. namespace {
  27. // Colour, message variants:
  28. // - white: No tests ran.
  29. // - red: Failed [both/all] N test cases, failed [both/all] M assertions.
  30. // - white: Passed [both/all] N test cases (no assertions).
  31. // - red: Failed N tests cases, failed M assertions.
  32. // - green: Passed [both/all] N tests cases with M assertions.
  33. void printTotals(std::ostream& out, const Totals& totals) {
  34. if (totals.testCases.total() == 0) {
  35. out << "No tests ran.";
  36. } else if (totals.testCases.failed == totals.testCases.total()) {
  37. Colour colour(Colour::ResultError);
  38. const std::string qualify_assertions_failed =
  39. totals.assertions.failed == totals.assertions.total() ?
  40. bothOrAll(totals.assertions.failed) : std::string();
  41. out <<
  42. "Failed " << bothOrAll(totals.testCases.failed)
  43. << pluralise(totals.testCases.failed, "test case") << ", "
  44. "failed " << qualify_assertions_failed <<
  45. pluralise(totals.assertions.failed, "assertion") << '.';
  46. } else if (totals.assertions.total() == 0) {
  47. out <<
  48. "Passed " << bothOrAll(totals.testCases.total())
  49. << pluralise(totals.testCases.total(), "test case")
  50. << " (no assertions).";
  51. } else if (totals.assertions.failed) {
  52. Colour colour(Colour::ResultError);
  53. out <<
  54. "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
  55. "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
  56. } else {
  57. Colour colour(Colour::ResultSuccess);
  58. out <<
  59. "Passed " << bothOrAll(totals.testCases.passed)
  60. << pluralise(totals.testCases.passed, "test case") <<
  61. " with " << pluralise(totals.assertions.passed, "assertion") << '.';
  62. }
  63. }
  64. // Implementation of CompactReporter formatting
  65. class AssertionPrinter {
  66. public:
  67. AssertionPrinter& operator= (AssertionPrinter const&) = delete;
  68. AssertionPrinter(AssertionPrinter const&) = delete;
  69. AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
  70. : stream(_stream)
  71. , result(_stats.assertionResult)
  72. , messages(_stats.infoMessages)
  73. , itMessage(_stats.infoMessages.begin())
  74. , printInfoMessages(_printInfoMessages) {}
  75. void print() {
  76. printSourceInfo();
  77. itMessage = messages.begin();
  78. switch (result.getResultType()) {
  79. case ResultWas::Ok:
  80. printResultType(Colour::ResultSuccess, 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(Colour::ResultSuccess, failedString() + std::string(" - but was ok"));
  91. else
  92. printResultType(Colour::Error, failedString());
  93. printOriginalExpression();
  94. printReconstructedExpression();
  95. printRemainingMessages();
  96. break;
  97. case ResultWas::ThrewException:
  98. printResultType(Colour::Error, failedString());
  99. printIssue("unexpected exception with message:");
  100. printMessage();
  101. printExpressionWas();
  102. printRemainingMessages();
  103. break;
  104. case ResultWas::FatalErrorCondition:
  105. printResultType(Colour::Error, failedString());
  106. printIssue("fatal error condition with message:");
  107. printMessage();
  108. printExpressionWas();
  109. printRemainingMessages();
  110. break;
  111. case ResultWas::DidntThrowException:
  112. printResultType(Colour::Error, failedString());
  113. printIssue("expected exception, got none");
  114. printExpressionWas();
  115. printRemainingMessages();
  116. break;
  117. case ResultWas::Info:
  118. printResultType(Colour::None, "info");
  119. printMessage();
  120. printRemainingMessages();
  121. break;
  122. case ResultWas::Warning:
  123. printResultType(Colour::None, "warning");
  124. printMessage();
  125. printRemainingMessages();
  126. break;
  127. case ResultWas::ExplicitFailure:
  128. printResultType(Colour::Error, failedString());
  129. printIssue("explicitly");
  130. printRemainingMessages(Colour::None);
  131. break;
  132. // These cases are here to prevent compiler warnings
  133. case ResultWas::Unknown:
  134. case ResultWas::FailureBit:
  135. case ResultWas::Exception:
  136. printResultType(Colour::Error, "** internal error **");
  137. break;
  138. }
  139. }
  140. private:
  141. void printSourceInfo() const {
  142. Colour colourGuard(Colour::FileName);
  143. stream << result.getSourceInfo() << ':';
  144. }
  145. void printResultType(Colour::Code colour, std::string const& passOrFail) const {
  146. if (!passOrFail.empty()) {
  147. {
  148. Colour colourGuard(colour);
  149. stream << ' ' << passOrFail;
  150. }
  151. stream << ':';
  152. }
  153. }
  154. void printIssue(std::string const& issue) const {
  155. stream << ' ' << issue;
  156. }
  157. void printExpressionWas() {
  158. if (result.hasExpression()) {
  159. stream << ';';
  160. {
  161. Colour colour(dimColour());
  162. stream << " expression was:";
  163. }
  164. printOriginalExpression();
  165. }
  166. }
  167. void printOriginalExpression() const {
  168. if (result.hasExpression()) {
  169. stream << ' ' << result.getExpression();
  170. }
  171. }
  172. void printReconstructedExpression() const {
  173. if (result.hasExpandedExpression()) {
  174. {
  175. Colour colour(dimColour());
  176. stream << " for: ";
  177. }
  178. stream << result.getExpandedExpression();
  179. }
  180. }
  181. void printMessage() {
  182. if (itMessage != messages.end()) {
  183. stream << " '" << itMessage->message << '\'';
  184. ++itMessage;
  185. }
  186. }
  187. void printRemainingMessages(Colour::Code colour = dimColour()) {
  188. if (itMessage == messages.end())
  189. return;
  190. const auto itEnd = messages.cend();
  191. const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
  192. {
  193. Colour colourGuard(colour);
  194. stream << " with " << pluralise(N, "message") << ':';
  195. }
  196. while (itMessage != itEnd) {
  197. // If this assertion is a warning ignore any INFO messages
  198. if (printInfoMessages || itMessage->type != ResultWas::Info) {
  199. printMessage();
  200. if (itMessage != itEnd) {
  201. Colour colourGuard(dimColour());
  202. stream << " and";
  203. }
  204. continue;
  205. }
  206. ++itMessage;
  207. }
  208. }
  209. private:
  210. std::ostream& stream;
  211. AssertionResult const& result;
  212. std::vector<MessageInfo> messages;
  213. std::vector<MessageInfo>::const_iterator itMessage;
  214. bool printInfoMessages;
  215. };
  216. } // anon namespace
  217. std::string CompactReporter::getDescription() {
  218. return "Reports test results on a single line, suitable for IDEs";
  219. }
  220. void CompactReporter::noMatchingTestCases( std::string const& spec ) {
  221. stream << "No test cases matched '" << spec << '\'' << std::endl;
  222. }
  223. void CompactReporter::assertionStarting( AssertionInfo const& ) {}
  224. bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
  225. AssertionResult const& result = _assertionStats.assertionResult;
  226. bool printInfoMessages = true;
  227. // Drop out if result was successful and we're not printing those
  228. if( !m_config->includeSuccessfulResults() && result.isOk() ) {
  229. if( result.getResultType() != ResultWas::Warning )
  230. return false;
  231. printInfoMessages = false;
  232. }
  233. AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
  234. printer.print();
  235. stream << std::endl;
  236. return true;
  237. }
  238. void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
  239. double dur = _sectionStats.durationInSeconds;
  240. if ( shouldShowDuration( *m_config, dur ) ) {
  241. stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
  242. }
  243. }
  244. void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
  245. printTotals( stream, _testRunStats.totals );
  246. stream << '\n' << std::endl;
  247. StreamingReporterBase::testRunEnded( _testRunStats );
  248. }
  249. CompactReporter::~CompactReporter() {}
  250. CATCH_REGISTER_REPORTER( "compact", CompactReporter )
  251. } // end namespace Catch