catch_reporter_console.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. /*
  2. * Created by Phil on 5/12/2012.
  3. * Copyright 2012 Two Blue Cubes Ltd. 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. #include "catch_reporter_console.h"
  9. #include "../internal/catch_reporter_registrars.hpp"
  10. #include "../internal/catch_console_colour.h"
  11. #include "../internal/catch_version.h"
  12. #include "../internal/catch_text.h"
  13. #include "../internal/catch_stringref.h"
  14. #include <cfloat>
  15. #include <cstdio>
  16. #if defined(_MSC_VER)
  17. #pragma warning(push)
  18. #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
  19. // Note that 4062 (not all labels are handled and default is missing) is enabled
  20. #endif
  21. #if defined(__clang__)
  22. # pragma clang diagnostic push
  23. // For simplicity, benchmarking-only helpers are always enabled
  24. # pragma clang diagnostic ignored "-Wunused-function"
  25. #endif
  26. namespace Catch {
  27. namespace {
  28. // Formatter impl for ConsoleReporter
  29. class ConsoleAssertionPrinter {
  30. public:
  31. ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
  32. ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
  33. ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
  34. : stream(_stream),
  35. stats(_stats),
  36. result(_stats.assertionResult),
  37. colour(Colour::None),
  38. message(result.getMessage()),
  39. messages(_stats.infoMessages),
  40. printInfoMessages(_printInfoMessages) {
  41. switch (result.getResultType()) {
  42. case ResultWas::Ok:
  43. colour = Colour::Success;
  44. passOrFail = "PASSED";
  45. //if( result.hasMessage() )
  46. if (_stats.infoMessages.size() == 1)
  47. messageLabel = "with message";
  48. if (_stats.infoMessages.size() > 1)
  49. messageLabel = "with messages";
  50. break;
  51. case ResultWas::ExpressionFailed:
  52. if (result.isOk()) {
  53. colour = Colour::Success;
  54. passOrFail = "FAILED - but was ok";
  55. } else {
  56. colour = Colour::Error;
  57. passOrFail = "FAILED";
  58. }
  59. if (_stats.infoMessages.size() == 1)
  60. messageLabel = "with message";
  61. if (_stats.infoMessages.size() > 1)
  62. messageLabel = "with messages";
  63. break;
  64. case ResultWas::ThrewException:
  65. colour = Colour::Error;
  66. passOrFail = "FAILED";
  67. messageLabel = "due to unexpected exception with ";
  68. if (_stats.infoMessages.size() == 1)
  69. messageLabel += "message";
  70. if (_stats.infoMessages.size() > 1)
  71. messageLabel += "messages";
  72. break;
  73. case ResultWas::FatalErrorCondition:
  74. colour = Colour::Error;
  75. passOrFail = "FAILED";
  76. messageLabel = "due to a fatal error condition";
  77. break;
  78. case ResultWas::DidntThrowException:
  79. colour = Colour::Error;
  80. passOrFail = "FAILED";
  81. messageLabel = "because no exception was thrown where one was expected";
  82. break;
  83. case ResultWas::Info:
  84. messageLabel = "info";
  85. break;
  86. case ResultWas::Warning:
  87. messageLabel = "warning";
  88. break;
  89. case ResultWas::ExplicitFailure:
  90. passOrFail = "FAILED";
  91. colour = Colour::Error;
  92. if (_stats.infoMessages.size() == 1)
  93. messageLabel = "explicitly with message";
  94. if (_stats.infoMessages.size() > 1)
  95. messageLabel = "explicitly with messages";
  96. break;
  97. // These cases are here to prevent compiler warnings
  98. case ResultWas::Unknown:
  99. case ResultWas::FailureBit:
  100. case ResultWas::Exception:
  101. passOrFail = "** internal error **";
  102. colour = Colour::Error;
  103. break;
  104. }
  105. }
  106. void print() const {
  107. printSourceInfo();
  108. if (stats.totals.assertions.total() > 0) {
  109. printResultType();
  110. printOriginalExpression();
  111. printReconstructedExpression();
  112. } else {
  113. stream << '\n';
  114. }
  115. printMessage();
  116. }
  117. private:
  118. void printResultType() const {
  119. if (!passOrFail.empty()) {
  120. Colour colourGuard(colour);
  121. stream << passOrFail << ":\n";
  122. }
  123. }
  124. void printOriginalExpression() const {
  125. if (result.hasExpression()) {
  126. Colour colourGuard(Colour::OriginalExpression);
  127. stream << " ";
  128. stream << result.getExpressionInMacro();
  129. stream << '\n';
  130. }
  131. }
  132. void printReconstructedExpression() const {
  133. if (result.hasExpandedExpression()) {
  134. stream << "with expansion:\n";
  135. Colour colourGuard(Colour::ReconstructedExpression);
  136. stream << Column(result.getExpandedExpression()).indent(2) << '\n';
  137. }
  138. }
  139. void printMessage() const {
  140. if (!messageLabel.empty())
  141. stream << messageLabel << ':' << '\n';
  142. for (auto const& msg : messages) {
  143. // If this assertion is a warning ignore any INFO messages
  144. if (printInfoMessages || msg.type != ResultWas::Info)
  145. stream << Column(msg.message).indent(2) << '\n';
  146. }
  147. }
  148. void printSourceInfo() const {
  149. Colour colourGuard(Colour::FileName);
  150. stream << result.getSourceInfo() << ": ";
  151. }
  152. std::ostream& stream;
  153. AssertionStats const& stats;
  154. AssertionResult const& result;
  155. Colour::Code colour;
  156. std::string passOrFail;
  157. std::string messageLabel;
  158. std::string message;
  159. std::vector<MessageInfo> messages;
  160. bool printInfoMessages;
  161. };
  162. std::size_t makeRatio(std::size_t number, std::size_t total) {
  163. std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
  164. return (ratio == 0 && number > 0) ? 1 : ratio;
  165. }
  166. std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) {
  167. if (i > j && i > k)
  168. return i;
  169. else if (j > k)
  170. return j;
  171. else
  172. return k;
  173. }
  174. struct ColumnInfo {
  175. enum Justification { Left, Right };
  176. std::string name;
  177. int width;
  178. Justification justification;
  179. };
  180. struct ColumnBreak {};
  181. struct RowBreak {};
  182. class Duration {
  183. enum class Unit {
  184. Auto,
  185. Nanoseconds,
  186. Microseconds,
  187. Milliseconds,
  188. Seconds,
  189. Minutes
  190. };
  191. static const uint64_t s_nanosecondsInAMicrosecond = 1000;
  192. static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
  193. static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
  194. static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
  195. double m_inNanoseconds;
  196. Unit m_units;
  197. public:
  198. explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
  199. : m_inNanoseconds(inNanoseconds),
  200. m_units(units) {
  201. if (m_units == Unit::Auto) {
  202. if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
  203. m_units = Unit::Nanoseconds;
  204. else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
  205. m_units = Unit::Microseconds;
  206. else if (m_inNanoseconds < s_nanosecondsInASecond)
  207. m_units = Unit::Milliseconds;
  208. else if (m_inNanoseconds < s_nanosecondsInAMinute)
  209. m_units = Unit::Seconds;
  210. else
  211. m_units = Unit::Minutes;
  212. }
  213. }
  214. auto value() const -> double {
  215. switch (m_units) {
  216. case Unit::Microseconds:
  217. return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
  218. case Unit::Milliseconds:
  219. return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
  220. case Unit::Seconds:
  221. return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
  222. case Unit::Minutes:
  223. return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
  224. default:
  225. return m_inNanoseconds;
  226. }
  227. }
  228. auto unitsAsString() const -> std::string {
  229. switch (m_units) {
  230. case Unit::Nanoseconds:
  231. return "ns";
  232. case Unit::Microseconds:
  233. return "us";
  234. case Unit::Milliseconds:
  235. return "ms";
  236. case Unit::Seconds:
  237. return "s";
  238. case Unit::Minutes:
  239. return "m";
  240. default:
  241. return "** internal error **";
  242. }
  243. }
  244. friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
  245. return os << duration.value() << ' ' << duration.unitsAsString();
  246. }
  247. };
  248. } // end anon namespace
  249. class TablePrinter {
  250. std::ostream& m_os;
  251. std::vector<ColumnInfo> m_columnInfos;
  252. std::ostringstream m_oss;
  253. int m_currentColumn = -1;
  254. bool m_isOpen = false;
  255. public:
  256. TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
  257. : m_os( os ),
  258. m_columnInfos( std::move( columnInfos ) ) {}
  259. auto columnInfos() const -> std::vector<ColumnInfo> const& {
  260. return m_columnInfos;
  261. }
  262. void open() {
  263. if (!m_isOpen) {
  264. m_isOpen = true;
  265. *this << RowBreak();
  266. Columns headerCols;
  267. Spacer spacer(2);
  268. for (auto const& info : m_columnInfos) {
  269. headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
  270. headerCols += spacer;
  271. }
  272. m_os << headerCols << '\n';
  273. m_os << Catch::getLineOfChars<'-'>() << '\n';
  274. }
  275. }
  276. void close() {
  277. if (m_isOpen) {
  278. *this << RowBreak();
  279. m_os << std::endl;
  280. m_isOpen = false;
  281. }
  282. }
  283. template<typename T>
  284. friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
  285. tp.m_oss << value;
  286. return tp;
  287. }
  288. friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
  289. auto colStr = tp.m_oss.str();
  290. const auto strSize = colStr.size();
  291. tp.m_oss.str("");
  292. tp.open();
  293. if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
  294. tp.m_currentColumn = -1;
  295. tp.m_os << '\n';
  296. }
  297. tp.m_currentColumn++;
  298. auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
  299. auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
  300. ? std::string(colInfo.width - (strSize + 1), ' ')
  301. : std::string();
  302. if (colInfo.justification == ColumnInfo::Left)
  303. tp.m_os << colStr << padding << ' ';
  304. else
  305. tp.m_os << padding << colStr << ' ';
  306. return tp;
  307. }
  308. friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
  309. if (tp.m_currentColumn > 0) {
  310. tp.m_os << '\n';
  311. tp.m_currentColumn = -1;
  312. }
  313. return tp;
  314. }
  315. };
  316. ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
  317. : StreamingReporterBase(config),
  318. m_tablePrinter(new TablePrinter(config.stream(),
  319. [&config]() -> std::vector<ColumnInfo> {
  320. if (config.fullConfig()->benchmarkNoAnalysis())
  321. {
  322. return{
  323. { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
  324. { " samples", 14, ColumnInfo::Right },
  325. { " iterations", 14, ColumnInfo::Right },
  326. { " mean", 14, ColumnInfo::Right }
  327. };
  328. }
  329. else
  330. {
  331. return{
  332. { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
  333. { "samples mean std dev", 14, ColumnInfo::Right },
  334. { "iterations low mean low std dev", 14, ColumnInfo::Right },
  335. { "estimated high mean high std dev", 14, ColumnInfo::Right }
  336. };
  337. }
  338. }())) {}
  339. ConsoleReporter::~ConsoleReporter() = default;
  340. std::string ConsoleReporter::getDescription() {
  341. return "Reports test results as plain lines of text";
  342. }
  343. void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
  344. stream << "No test cases matched '" << spec << '\'' << std::endl;
  345. }
  346. void ConsoleReporter::reportInvalidArguments(std::string const&arg){
  347. stream << "Invalid Filter: " << arg << std::endl;
  348. }
  349. void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
  350. bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
  351. AssertionResult const& result = _assertionStats.assertionResult;
  352. bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
  353. // Drop out if result was successful but we're not printing them.
  354. if (!includeResults && result.getResultType() != ResultWas::Warning)
  355. return false;
  356. lazyPrint();
  357. ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
  358. printer.print();
  359. stream << std::endl;
  360. return true;
  361. }
  362. void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
  363. m_tablePrinter->close();
  364. m_headerPrinted = false;
  365. StreamingReporterBase::sectionStarting(_sectionInfo);
  366. }
  367. void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
  368. m_tablePrinter->close();
  369. if (_sectionStats.missingAssertions) {
  370. lazyPrint();
  371. Colour colour(Colour::ResultError);
  372. if (m_sectionStack.size() > 1)
  373. stream << "\nNo assertions in section";
  374. else
  375. stream << "\nNo assertions in test case";
  376. stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
  377. }
  378. double dur = _sectionStats.durationInSeconds;
  379. if (shouldShowDuration(*m_config, dur)) {
  380. stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
  381. }
  382. if (m_headerPrinted) {
  383. m_headerPrinted = false;
  384. }
  385. StreamingReporterBase::sectionEnded(_sectionStats);
  386. }
  387. #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
  388. void ConsoleReporter::benchmarkPreparing(std::string const& name) {
  389. lazyPrintWithoutClosingBenchmarkTable();
  390. auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
  391. bool firstLine = true;
  392. for (auto line : nameCol) {
  393. if (!firstLine)
  394. (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
  395. else
  396. firstLine = false;
  397. (*m_tablePrinter) << line << ColumnBreak();
  398. }
  399. }
  400. void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
  401. (*m_tablePrinter) << info.samples << ColumnBreak()
  402. << info.iterations << ColumnBreak();
  403. if (!m_config->benchmarkNoAnalysis())
  404. (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
  405. }
  406. void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
  407. if (m_config->benchmarkNoAnalysis())
  408. {
  409. (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
  410. }
  411. else
  412. {
  413. (*m_tablePrinter) << ColumnBreak()
  414. << Duration(stats.mean.point.count()) << ColumnBreak()
  415. << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
  416. << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
  417. << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
  418. << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
  419. << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
  420. }
  421. }
  422. void ConsoleReporter::benchmarkFailed(std::string const& error) {
  423. Colour colour(Colour::Red);
  424. (*m_tablePrinter)
  425. << "Benchmark failed (" << error << ')'
  426. << ColumnBreak() << RowBreak();
  427. }
  428. #endif // CATCH_CONFIG_ENABLE_BENCHMARKING
  429. void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
  430. m_tablePrinter->close();
  431. StreamingReporterBase::testCaseEnded(_testCaseStats);
  432. m_headerPrinted = false;
  433. }
  434. void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
  435. if (currentGroupInfo.used) {
  436. printSummaryDivider();
  437. stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
  438. printTotals(_testGroupStats.totals);
  439. stream << '\n' << std::endl;
  440. }
  441. StreamingReporterBase::testGroupEnded(_testGroupStats);
  442. }
  443. void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
  444. printTotalsDivider(_testRunStats.totals);
  445. printTotals(_testRunStats.totals);
  446. stream << std::endl;
  447. StreamingReporterBase::testRunEnded(_testRunStats);
  448. }
  449. void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
  450. StreamingReporterBase::testRunStarting(_testInfo);
  451. printTestFilters();
  452. }
  453. void ConsoleReporter::lazyPrint() {
  454. m_tablePrinter->close();
  455. lazyPrintWithoutClosingBenchmarkTable();
  456. }
  457. void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
  458. if (!currentTestRunInfo.used)
  459. lazyPrintRunInfo();
  460. if (!currentGroupInfo.used)
  461. lazyPrintGroupInfo();
  462. if (!m_headerPrinted) {
  463. printTestCaseAndSectionHeader();
  464. m_headerPrinted = true;
  465. }
  466. }
  467. void ConsoleReporter::lazyPrintRunInfo() {
  468. stream << '\n' << getLineOfChars<'~'>() << '\n';
  469. Colour colour(Colour::SecondaryText);
  470. stream << currentTestRunInfo->name
  471. << " is a Catch v" << libraryVersion() << " host application.\n"
  472. << "Run with -? for options\n\n";
  473. if (m_config->rngSeed() != 0)
  474. stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
  475. currentTestRunInfo.used = true;
  476. }
  477. void ConsoleReporter::lazyPrintGroupInfo() {
  478. if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
  479. printClosedHeader("Group: " + currentGroupInfo->name);
  480. currentGroupInfo.used = true;
  481. }
  482. }
  483. void ConsoleReporter::printTestCaseAndSectionHeader() {
  484. assert(!m_sectionStack.empty());
  485. printOpenHeader(currentTestCaseInfo->name);
  486. if (m_sectionStack.size() > 1) {
  487. Colour colourGuard(Colour::Headers);
  488. auto
  489. it = m_sectionStack.begin() + 1, // Skip first section (test case)
  490. itEnd = m_sectionStack.end();
  491. for (; it != itEnd; ++it)
  492. printHeaderString(it->name, 2);
  493. }
  494. SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
  495. stream << getLineOfChars<'-'>() << '\n';
  496. Colour colourGuard(Colour::FileName);
  497. stream << lineInfo << '\n';
  498. stream << getLineOfChars<'.'>() << '\n' << std::endl;
  499. }
  500. void ConsoleReporter::printClosedHeader(std::string const& _name) {
  501. printOpenHeader(_name);
  502. stream << getLineOfChars<'.'>() << '\n';
  503. }
  504. void ConsoleReporter::printOpenHeader(std::string const& _name) {
  505. stream << getLineOfChars<'-'>() << '\n';
  506. {
  507. Colour colourGuard(Colour::Headers);
  508. printHeaderString(_name);
  509. }
  510. }
  511. // if string has a : in first line will set indent to follow it on
  512. // subsequent lines
  513. void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
  514. std::size_t i = _string.find(": ");
  515. if (i != std::string::npos)
  516. i += 2;
  517. else
  518. i = 0;
  519. stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n';
  520. }
  521. struct SummaryColumn {
  522. SummaryColumn( std::string _label, Colour::Code _colour )
  523. : label( std::move( _label ) ),
  524. colour( _colour ) {}
  525. SummaryColumn addRow( std::size_t count ) {
  526. ReusableStringStream rss;
  527. rss << count;
  528. std::string row = rss.str();
  529. for (auto& oldRow : rows) {
  530. while (oldRow.size() < row.size())
  531. oldRow = ' ' + oldRow;
  532. while (oldRow.size() > row.size())
  533. row = ' ' + row;
  534. }
  535. rows.push_back(row);
  536. return *this;
  537. }
  538. std::string label;
  539. Colour::Code colour;
  540. std::vector<std::string> rows;
  541. };
  542. void ConsoleReporter::printTotals( Totals const& totals ) {
  543. if (totals.testCases.total() == 0) {
  544. stream << Colour(Colour::Warning) << "No tests ran\n";
  545. } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
  546. stream << Colour(Colour::ResultSuccess) << "All tests passed";
  547. stream << " ("
  548. << pluralise(totals.assertions.passed, "assertion") << " in "
  549. << pluralise(totals.testCases.passed, "test case") << ')'
  550. << '\n';
  551. } else {
  552. std::vector<SummaryColumn> columns;
  553. columns.push_back(SummaryColumn("", Colour::None)
  554. .addRow(totals.testCases.total())
  555. .addRow(totals.assertions.total()));
  556. columns.push_back(SummaryColumn("passed", Colour::Success)
  557. .addRow(totals.testCases.passed)
  558. .addRow(totals.assertions.passed));
  559. columns.push_back(SummaryColumn("failed", Colour::ResultError)
  560. .addRow(totals.testCases.failed)
  561. .addRow(totals.assertions.failed));
  562. columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure)
  563. .addRow(totals.testCases.failedButOk)
  564. .addRow(totals.assertions.failedButOk));
  565. printSummaryRow("test cases", columns, 0);
  566. printSummaryRow("assertions", columns, 1);
  567. }
  568. }
  569. void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
  570. for (auto col : cols) {
  571. std::string value = col.rows[row];
  572. if (col.label.empty()) {
  573. stream << label << ": ";
  574. if (value != "0")
  575. stream << value;
  576. else
  577. stream << Colour(Colour::Warning) << "- none -";
  578. } else if (value != "0") {
  579. stream << Colour(Colour::LightGrey) << " | ";
  580. stream << Colour(col.colour)
  581. << value << ' ' << col.label;
  582. }
  583. }
  584. stream << '\n';
  585. }
  586. void ConsoleReporter::printTotalsDivider(Totals const& totals) {
  587. if (totals.testCases.total() > 0) {
  588. std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
  589. std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
  590. std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
  591. while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
  592. findMax(failedRatio, failedButOkRatio, passedRatio)++;
  593. while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
  594. findMax(failedRatio, failedButOkRatio, passedRatio)--;
  595. stream << Colour(Colour::Error) << std::string(failedRatio, '=');
  596. stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '=');
  597. if (totals.testCases.allPassed())
  598. stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
  599. else
  600. stream << Colour(Colour::Success) << std::string(passedRatio, '=');
  601. } else {
  602. stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
  603. }
  604. stream << '\n';
  605. }
  606. void ConsoleReporter::printSummaryDivider() {
  607. stream << getLineOfChars<'-'>() << '\n';
  608. }
  609. void ConsoleReporter::printTestFilters() {
  610. if (m_config->testSpec().hasFilters()) {
  611. Colour guard(Colour::BrightYellow);
  612. stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
  613. }
  614. }
  615. CATCH_REGISTER_REPORTER("console", ConsoleReporter)
  616. } // end namespace Catch
  617. #if defined(_MSC_VER)
  618. #pragma warning(pop)
  619. #endif
  620. #if defined(__clang__)
  621. # pragma clang diagnostic pop
  622. #endif