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