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