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