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 // using messages.end() directly yields (or auto) compilation error:
213 std::vector<MessageInfo>::const_iterator itEnd = messages.end();
214 const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
215
216 {
217 Colour colourGuard(colour);
218 stream << " with " << pluralise(N, "message") << ':';
219 }
220
221 for (; itMessage != itEnd; ) {
222 // If this assertion is a warning ignore any INFO messages
223 if (printInfoMessages || itMessage->type != ResultWas::Info) {
224 stream << " '" << itMessage->message << '\'';
225 if (++itMessage != itEnd) {
226 Colour colourGuard(dimColour());
227 stream << " and";
228 }
229 }
230 }
231 }
232
233 private:
234 std::ostream& stream;
235 AssertionResult const& result;
236 std::vector<MessageInfo> messages;
237 std::vector<MessageInfo>::const_iterator itMessage;
238 bool printInfoMessages;
239 };
240
241 } // anon namespace
242
getDescription()243 std::string CompactReporter::getDescription() {
244 return "Reports test results on a single line, suitable for IDEs";
245 }
246
getPreferences() const247 ReporterPreferences CompactReporter::getPreferences() const {
248 return m_reporterPrefs;
249 }
250
noMatchingTestCases(std::string const & spec)251 void CompactReporter::noMatchingTestCases( std::string const& spec ) {
252 stream << "No test cases matched '" << spec << '\'' << std::endl;
253 }
254
assertionStarting(AssertionInfo const &)255 void CompactReporter::assertionStarting( AssertionInfo const& ) {}
256
assertionEnded(AssertionStats const & _assertionStats)257 bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
258 AssertionResult const& result = _assertionStats.assertionResult;
259
260 bool printInfoMessages = true;
261
262 // Drop out if result was successful and we're not printing those
263 if( !m_config->includeSuccessfulResults() && result.isOk() ) {
264 if( result.getResultType() != ResultWas::Warning )
265 return false;
266 printInfoMessages = false;
267 }
268
269 AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
270 printer.print();
271
272 stream << std::endl;
273 return true;
274 }
275
sectionEnded(SectionStats const & _sectionStats)276 void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
277 if (m_config->showDurations() == ShowDurations::Always) {
278 stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
279 }
280 }
281
testRunEnded(TestRunStats const & _testRunStats)282 void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
283 printTotals( stream, _testRunStats.totals );
284 stream << '\n' << std::endl;
285 StreamingReporterBase::testRunEnded( _testRunStats );
286 }
287
~CompactReporter()288 CompactReporter::~CompactReporter() {}
289
290 CATCH_REGISTER_REPORTER( "compact", CompactReporter )
291
292 } // end namespace Catch
293