• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9 #include "catch_reporter_console.h"
10 
11 #include "../internal/catch_reporter_registrars.hpp"
12 #include "internal/catch_console_colour.h"
13 #include "../internal/catch_version.h"
14 #include "../internal/catch_text.h"
15 #include "../internal/catch_stringref.h"
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 
28 namespace Catch {
29 
30 namespace {
31 
32 // Formatter impl for ConsoleReporter
33 class ConsoleAssertionPrinter {
34 public:
35     ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
36     ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
ConsoleAssertionPrinter(std::ostream & _stream,AssertionStats const & _stats,bool _printInfoMessages)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;
68         case ResultWas::ThrewException:
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;
77         case ResultWas::FatalErrorCondition:
78             colour = Colour::Error;
79             passOrFail = "FAILED";
80             messageLabel = "due to a fatal error condition";
81             break;
82         case ResultWas::DidntThrowException:
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;
90         case ResultWas::Warning:
91             messageLabel = "warning";
92             break;
93         case ResultWas::ExplicitFailure:
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
102         case ResultWas::Unknown:
103         case ResultWas::FailureBit:
104         case ResultWas::Exception:
105             passOrFail = "** internal error **";
106             colour = Colour::Error;
107             break;
108         }
109     }
110 
print() const111     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 
123 private:
printResultType() const124     void printResultType() const {
125         if (!passOrFail.empty()) {
126             Colour colourGuard(colour);
127             stream << passOrFail << ":\n";
128         }
129     }
printOriginalExpression() const130     void printOriginalExpression() const {
131         if (result.hasExpression()) {
132             Colour colourGuard(Colour::OriginalExpression);
133             stream << "  ";
134             stream << result.getExpressionInMacro();
135             stream << '\n';
136         }
137     }
printReconstructedExpression() const138     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     }
printMessage() const145     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     }
printSourceInfo() const154     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 
makeRatio(std::size_t number,std::size_t total)170 std::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 
findMax(std::size_t & i,std::size_t & j,std::size_t & k)175 std::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 
184 struct ColumnInfo {
185     enum Justification { Left, Right };
186     std::string name;
187     int width;
188     Justification justification;
189 };
190 struct ColumnBreak {};
191 struct RowBreak {};
192 
193 class 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 
210 public:
Duration(uint64_t inNanoseconds,Unit units=Unit::Auto)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 
value() const229     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     }
unitsAsString() const243     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     }
operator <<(std::ostream & os,Duration const & duration)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 
266 class TablePrinter {
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 
273 public:
TablePrinter(std::ostream & os,std::vector<ColumnInfo> columnInfos)274     TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
275     :   m_os( os ),
276         m_columnInfos( std::move( columnInfos ) ) {}
277 
columnInfos() const278     auto columnInfos() const -> std::vector<ColumnInfo> const& {
279         return m_columnInfos;
280     }
281 
open()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     }
close()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>
operator <<(TablePrinter & tp,T const & value)301     friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
302         tp.m_oss << value;
303         return tp;
304     }
305 
operator <<(TablePrinter & tp,ColumnBreak)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 
operator <<(TablePrinter & tp,RowBreak)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 
ConsoleReporter(ReporterConfig const & config)338 ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
339     : StreamingReporterBase(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     })) {}
347 ConsoleReporter::~ConsoleReporter() = default;
348 
getDescription()349 std::string ConsoleReporter::getDescription() {
350     return "Reports test results as plain lines of text";
351 }
352 
noMatchingTestCases(std::string const & spec)353 void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
354     stream << "No test cases matched '" << spec << '\'' << std::endl;
355 }
356 
assertionStarting(AssertionInfo const &)357 void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
358 
assertionEnded(AssertionStats const & _assertionStats)359 bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
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 
sectionStarting(SectionInfo const & _sectionInfo)376 void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
377     m_headerPrinted = false;
378     StreamingReporterBase::sectionStarting(_sectionInfo);
379 }
sectionEnded(SectionStats const & _sectionStats)380 void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
381     m_tablePrinter->close();
382     if (_sectionStats.missingAssertions) {
383         lazyPrint();
384         Colour colour(Colour::ResultError);
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     }
397     StreamingReporterBase::sectionEnded(_sectionStats);
398 }
399 
400 
benchmarkStarting(BenchmarkInfo const & info)401 void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
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 }
benchmarkEnded(BenchmarkStats const & stats)416 void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
417     Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
418     (*m_tablePrinter)
419         << stats.iterations << ColumnBreak()
420         << stats.elapsedTimeInNanoseconds << ColumnBreak()
421         << average << ColumnBreak();
422 }
423 
testCaseEnded(TestCaseStats const & _testCaseStats)424 void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
425     m_tablePrinter->close();
426     StreamingReporterBase::testCaseEnded(_testCaseStats);
427     m_headerPrinted = false;
428 }
testGroupEnded(TestGroupStats const & _testGroupStats)429 void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
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     }
436     StreamingReporterBase::testGroupEnded(_testGroupStats);
437 }
testRunEnded(TestRunStats const & _testRunStats)438 void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
439     printTotalsDivider(_testRunStats.totals);
440     printTotals(_testRunStats.totals);
441     stream << std::endl;
442     StreamingReporterBase::testRunEnded(_testRunStats);
443 }
testRunStarting(TestRunInfo const & _testInfo)444 void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
445     StreamingReporterBase::testRunStarting(_testInfo);
446     printTestFilters();
447 }
448 
lazyPrint()449 void ConsoleReporter::lazyPrint() {
450 
451     m_tablePrinter->close();
452     lazyPrintWithoutClosingBenchmarkTable();
453 }
454 
lazyPrintWithoutClosingBenchmarkTable()455 void 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 }
lazyPrintRunInfo()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 
474     if (m_config->rngSeed() != 0)
475         stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
476 
477     currentTestRunInfo.used = true;
478 }
lazyPrintGroupInfo()479 void ConsoleReporter::lazyPrintGroupInfo() {
480     if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
481         printClosedHeader("Group: " + currentGroupInfo->name);
482         currentGroupInfo.used = true;
483     }
484 }
printTestCaseAndSectionHeader()485 void 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 
printClosedHeader(std::string const & _name)509 void ConsoleReporter::printClosedHeader(std::string const& _name) {
510     printOpenHeader(_name);
511     stream << getLineOfChars<'.'>() << '\n';
512 }
printOpenHeader(std::string const & _name)513 void 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
printHeaderString(std::string const & _string,std::size_t indent)523 void 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 
532 struct SummaryColumn {
533 
SummaryColumnCatch::SummaryColumn534     SummaryColumn( std::string _label, Colour::Code _colour )
535     :   label( std::move( _label ) ),
536         colour( _colour ) {}
addRowCatch::SummaryColumn537     SummaryColumn addRow( std::size_t count ) {
538         ReusableStringStream rss;
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;
552     Colour::Code colour;
553     std::vector<std::string> rows;
554 
555 };
556 
printTotals(Totals const & totals)557 void 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 }
printSummaryRow(std::string const & label,std::vector<SummaryColumn> const & cols,std::size_t row)586 void 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 
printTotalsDivider(Totals const & totals)604 void 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 }
printSummaryDivider()625 void ConsoleReporter::printSummaryDivider() {
626     stream << getLineOfChars<'-'>() << '\n';
627 }
628 
printTestFilters()629 void ConsoleReporter::printTestFilters() {
630     if (m_config->testSpec().hasFilters())
631         stream << Colour(Colour::BrightYellow) << "Filters: " << serializeFilters( m_config->getTestsOrTags() ) << '\n';
632 }
633 
634 CATCH_REGISTER_REPORTER("console", ConsoleReporter)
635 
636 } // end namespace Catch
637 
638 #if defined(_MSC_VER)
639 #pragma warning(pop)
640 #endif
641