OTest2
A C++ testing framework
reporterconsole.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019 Ondrej Starek
3  *
4  * This file is part of OTest2.
5  *
6  * OTest2 is free software: you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License,
9  * or (at your option) any later version.
10  *
11  * OTest2 is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with OTest2. If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include <reporterconsole.h>
20 
21 #include <assert.h>
22 #include <iomanip>
23 #include <iostream>
24 #include <sstream>
25 #include <utility>
26 
27 #include <assertbufferstr.h>
28 #include <context.h>
29 #include <objectpath.h>
30 #include <parameters.h>
31 #include <reporterstatistics.h>
32 #include <terminaldriver.h>
33 #include <utils.h>
34 
35 namespace OTest2 {
36 
37 namespace {
38 
39 enum : int {
40  WIDTH = 80,
41  INDENT_SPACE = 2,
42 };
43 
44 void printIndent(
45  std::ostream& os_,
46  int indent_) {
47  for(int i_(0); i_ < indent_; ++i_)
48  for(int j_(0); j_ < INDENT_SPACE; ++j_)
49  os_ << ' ';
50 }
51 
52 void printHR(
53  std::ostream& os_,
54  char rule_char_,
55  const std::string& label_,
56  int indent_) {
57  /* -- label width */
58  int label_width_(0);
59  if(!label_.empty()) {
60  label_width_ = static_cast<int>(label_.size()) + 2;
61  }
62 
63  /* -- compute length of the left and righ part of the rule */
64  const int width_(WIDTH - 2 - INDENT_SPACE * indent_);
65  int left_width_((width_ - label_width_) / 2);
66  int right_width_(width_ - label_width_ - left_width_);
67 
68  /* -- print indentation */
69  printIndent(os_, indent_);
70 
71  /* -- print left rule */
72  os_ << ' ';
73  for(int i_(0); i_ < left_width_; ++i_)
74  os_ << rule_char_;
75 
76  /* -- print label */
77  if(!label_.empty()) {
78  os_ << ' ' << label_ << ' ';
79  }
80 
81  /* -- print right rule */
82  for(int i_(0); i_ < right_width_; ++i_)
83  os_ << rule_char_;
84 
85  os_ << std::endl;
86 }
87 
88 void printResultLine(
89  std::ostream& os_,
90  TerminalDriver& driver_,
91  const std::string& label_,
92  bool result_,
93  int indent_) {
94  int label_width_(static_cast<int>(label_.size()));
95  int free_space_width_(WIDTH - 4 - INDENT_SPACE * indent_ - label_width_ - 8);
96 
97  printIndent(os_, indent_);
98 
99  os_ << " " << label_;
100  for(int i_(0); i_ < free_space_width_; ++i_)
101  os_ << ' ';
102  os_ << '[';
103 
104  if(result_) {
105  driver_.setForeground(*os_.rdbuf(), Color::GREEN);
106  os_ << "Passed";
107  }
108  else {
109  driver_.setForeground(*os_.rdbuf(), Color::RED);
110  os_ << "Failed";
111  }
112  driver_.cleanAttributes(*os_.rdbuf());
113  os_ << ']' << std::endl;
114 }
115 
116 void printTotalResultLine(
117  std::ostream& os_,
118  const std::string& label_,
119  const std::string& ok_,
120  const std::string& failed_,
121  const std::string& total_) {
122  int label_width_(static_cast<int>(label_.size()));
123  int ok_width_(static_cast<int>(ok_.size()));
124  int failed_width_(static_cast<int>(failed_.size()));
125 
126  enum {
127  OK_COLUMN = 20,
128  FAILED_COLUMN = 40,
129  TOTAL_COLUMN = 60,
130  };
131 
132  os_ << " " << label_;
133  int i_(2 + label_width_);
134  for(; i_ < OK_COLUMN; ++i_)
135  os_ << ' ';
136  os_ << ok_;
137  for(i_ += ok_width_; i_ < FAILED_COLUMN; ++i_)
138  os_ << ' ';
139  os_ << failed_;
140  for(i_ += failed_width_; i_ < TOTAL_COLUMN; ++i_)
141  os_ << ' ';
142  os_ << total_ << std::endl;
143 }
144 
145 void printTotalResult(
146  std::ostream& os_,
147  const std::string& label_,
148  int ok_,
149  int failed_,
150  int total_) {
151  std::ostringstream ok_str_;
152  ok_str_ << std::setw(8) << ok_;
153  std::ostringstream failed_str_;
154  failed_str_ << std::setw(8) << failed_;
155  std::ostringstream total_str_;
156  total_str_ << std::setw(8) << total_;
157  printTotalResultLine(
158  os_, label_, ok_str_.str(), failed_str_.str(), total_str_.str());
159 }
160 
161 void printTotalErrors(
162  std::ostream& os_,
163  const std::string& label_,
164  int errors_) {
165  std::ostringstream errors_str_;
166  errors_str_ << std::setw(8) << errors_;
167  printTotalResultLine(os_, label_, " ", " ", errors_str_.str());
168 }
169 
170 } /* -- namespace */
171 
172 struct ReporterConsole::Impl : public AssertBufferListener {
173  private:
174  class AssertBuffer : public AssertBufferStr {
175  private:
176  Impl* pimpl;
177 
178  public:
179  /* -- avoid copying */
180  AssertBuffer(
181  const AssertBuffer&) = delete;
183  const AssertBuffer&) = delete;
184 
185  explicit AssertBuffer(
186  Impl* pimpl_);
187  virtual ~AssertBuffer();
188 
189  /* -- setting of text attributes */
190  virtual void setForeground(
191  Color color_) override;
192  virtual void setBackground(
193  Color color_) override;
194  virtual void setTextStyle(
195  Style style_) override;
196  virtual void resetAttributes() override;
197  };
198 
199  static int selectHandle(
200  std::ostream* os_);
201 
202  public:
203  ReporterConsole* owner;
204  std::ostream* os;
205  TerminalDriver term_driver;
206  ReporterStatistics statistics;
207 
208  /* -- reporter state */
209  int level;
210 
211  /* -- assertion printing */
212  bool verbose;
213  bool hide_location;
214  int indent;
215  std::pair<int, char> stacked_hr;
216 
217  /* -- buffer for assertion messages */
218  AssertBufferStrPtr assert_buffer;
219 
220  /* -- avoid copying */
221  Impl(
222  const Impl&) = delete;
223  Impl& operator =(
224  const Impl&) = delete;
225 
226  explicit Impl(
227  ReporterConsole* owner_,
228  std::ostream* os_,
229  bool verbose_,
230  bool hide_location_);
231  virtual ~Impl();
232 
233  void printStackedHR();
234  void resetStackedHR();
235 
236  void printAdditionalMessage(
237  bool condition_,
238  const std::string& message_);
239 
240  /* -- assertion buffer listener */
241  virtual void assertionOpeningMessage(
242  const Context& context_,
243  const AssertBufferAssertData& data_,
244  const std::string& message_) override;
245  virtual void assertionAdditionalMessage(
246  const Context& context_,
247  const AssertBufferAssertData& data_,
248  const std::string& message_) override;
249  virtual void assertionClose(
250  const Context& context_,
251  const AssertBufferAssertData& data_) override;
252  virtual void errorOpeningMessage(
253  const Context& context_,
254  const std::string& message_) override;
255  virtual void errorAdditionalMessage(
256  const Context& context_,
257  const std::string& message_) override;
258  virtual void errorClose(
259  const Context& context_) override;
260 };
261 
262 ReporterConsole::Impl::AssertBuffer::AssertBuffer(
263  Impl* pimpl_) :
264  AssertBufferStr(pimpl_),
265  pimpl(pimpl_) {
266 
267 }
268 
269 ReporterConsole::Impl::AssertBuffer::~AssertBuffer() = default;
270 
271 void ReporterConsole::Impl::AssertBuffer::setForeground(
272  Color color_) {
273  pimpl->term_driver.setForeground(*this, color_);
274 }
275 
276 void ReporterConsole::Impl::AssertBuffer::setBackground(
277  Color color_) {
278  pimpl->term_driver.setBackground(*this, color_);
279 }
280 
281 void ReporterConsole::Impl::AssertBuffer::setTextStyle(
282  Style style_) {
283  pimpl->term_driver.setTextStyle(*this, style_);
284 }
285 
286 void ReporterConsole::Impl::AssertBuffer::resetAttributes() {
287  pimpl->term_driver.cleanAttributes(*this);
288 }
289 
290 ReporterConsole::Impl::Impl(
291  ReporterConsole* owner_,
292  std::ostream* os_,
293  bool verbose_,
294  bool hide_location_) :
295  owner(owner_),
296  os(os_),
297  term_driver(selectHandle(os_)),
298  level(0),
299  verbose(verbose_),
300  hide_location(hide_location_),
301  indent(-1),
302  stacked_hr(0, '='),
303  assert_buffer(std::make_shared<AssertBuffer>(this)) {
304  assert(os != nullptr);
305 
306 }
307 
308 ReporterConsole::Impl::~Impl() = default;
309 
310 int ReporterConsole::Impl::selectHandle(
311  std::ostream* os_) {
312  if(os_ == &std::cout)
313  return 1;
314  else if(os_ == &std::cerr)
315  return 2;
316  else
317  return -1;
318 }
319 
320 void ReporterConsole::Impl::printStackedHR() {
321  if(stacked_hr.first >= 0)
322  printHR(*os, stacked_hr.second, "", stacked_hr.first);
323  stacked_hr = {-1, ' '};
324 }
325 
326 void ReporterConsole::Impl::resetStackedHR() {
327  stacked_hr = {-1, ' '};
328 }
329 
330 void ReporterConsole::Impl::printAdditionalMessage(
331  bool condition_,
332  const std::string& message_) {
333  if(verbose || !condition_) {
334  std::istringstream iss_(message_);
335  std::string line_;
336  while(std::getline(iss_, line_))
337  *os << " " << line_ << std::endl;
338  }
339 }
340 
341 void ReporterConsole::Impl::assertionOpeningMessage(
342  const Context& context_,
343  const AssertBufferAssertData& data_,
344  const std::string& message_) {
345  printStackedHR();
346 
347  /* -- print the assertion status */
348  if(verbose || !data_.condition) {
349  /* -- make the opening message more visible */
350  term_driver.setTextStyle(*os->rdbuf(), Style::BOLD);
351 
352  /* -- print the message */
353  *os << '[';
354  if(!hide_location)
355  *os << data_.file << ':' << data_.line;
356  else
357  *os << "...";
358  *os << "] "
359  << context_.object_path->getCurrentPath() << ": " << message_
360  << std::endl;
361 
362  /* -- clean text attributes */
363  term_driver.cleanAttributes(*os->rdbuf());
364  }
365 }
366 
367 void ReporterConsole::Impl::assertionAdditionalMessage(
368  const Context& context_,
369  const AssertBufferAssertData& data_,
370  const std::string& message_) {
371  printAdditionalMessage(data_.condition, message_);
372 }
373 
374 void ReporterConsole::Impl::assertionClose(
375  const Context& context_,
376  const AssertBufferAssertData& data_) {
377  /* -- nothing to do */
378 }
379 
380 void ReporterConsole::Impl::errorOpeningMessage(
381  const Context& context_,
382  const std::string& message_) {
383  printStackedHR();
384 
385  /* -- internal error */
386  *os << "error in " << context_.object_path->getCurrentPath() << ": " << message_
387  << std::endl;
388 }
389 
390 void ReporterConsole::Impl::errorAdditionalMessage(
391  const Context& context_,
392  const std::string& message_) {
393  printAdditionalMessage(false, message_);
394 }
395 
396 void ReporterConsole::Impl::errorClose(
397  const Context& context_) {
398  /* -- nothing to do */
399 }
400 
402  std::ostream* os_,
403  bool verbose_,
404  bool hide_location_) :
405  pimpl(new Impl(this, os_, verbose_, hide_location_)) {
406 
407 }
408 
410  odelete(pimpl);
411 }
412 
414  const Context& context_,
415  const std::string& name_,
416  const Parameters& params_) {
417 
418 }
419 
421  const Context& context_,
422  const std::string& name_,
423  const Parameters& params_) {
424  /* -- print the separator */
425  ++pimpl->indent;
426  pimpl->resetStackedHR();
427  printHR(*pimpl->os, '=', params_.mixWithName(name_), pimpl->indent);
428 
429  /* -- increase level of nesting */
430  ++pimpl->level;
431 }
432 
434  const Context& context_,
435  const std::string& name_,
436  const Parameters& params_) {
437  /* -- correct indentation of a standalone test case */
438  if(pimpl->level == 0)
439  pimpl->indent = 0;
440  pimpl->printStackedHR();
441 
442  /* -- increase level of nesting */
443  ++pimpl->level;
444 }
445 
447  const Context& context_,
448  const std::string& name_) {
449 
450 }
451 
453  const Context& context_,
454  bool condition_,
455  const std::string& file_,
456  int lineno_) {
457  /* -- adjust the statistics */
458  pimpl->statistics.reportAssertion(condition_);
459 
460  /* -- open the assertion buffer */
461  pimpl->assert_buffer->openAssertion({condition_, file_, lineno_});
462  return pimpl->assert_buffer;
463 }
464 
466  const Context& context_) {
467  /* -- adjust the statistics */
468  pimpl->statistics.reportError();
469 
470  /* -- open the assertion buffer */
471  pimpl->assert_buffer->openError();
472  return pimpl->assert_buffer;
473 }
474 
476  const Context& context_,
477  const std::string& name_,
478  bool result_) {
479  /* -- nothing to do */
480 }
481 
483  const Context& context_,
484  const std::string& name_,
485  const Parameters& params_,
486  bool result_) {
487  pimpl->statistics.reportCase(result_);
488 
489  /* -- print result of the test case */
490  pimpl->printStackedHR();
491  printResultLine(*pimpl->os, pimpl->term_driver, params_.mixWithName(name_), result_, pimpl->indent);
492 
493  /* -- decrease level of nesting and indentation */
494  --pimpl->level;
495  if(pimpl->level == 0)
496  --pimpl->indent;
497 }
498 
500  const Context& context_,
501  const std::string& name_,
502  const Parameters& params_,
503  bool result_) {
504  pimpl->statistics.reportSuite(result_);
505 
506  /* -- print result of the suite */
507  pimpl->resetStackedHR();
508  printHR(*pimpl->os, '-', "", pimpl->indent);
509  printResultLine(*pimpl->os, pimpl->term_driver, "Suite total", result_, pimpl->indent);
510 
511  /* -- stack the suite ending separator */
512  pimpl->stacked_hr = {pimpl->indent, '='};
513 
514  /* -- decrease level of nesting and indentation */
515  --pimpl->level;
516  --pimpl->indent;
517 }
518 
520  const Context& context_,
521  const std::string& name_,
522  const Parameters& params_,
523  bool result_) {
524  pimpl->resetStackedHR();
525  printHR(*pimpl->os, '=', "Test results", 0);
526  printTotalResultLine(
527  *pimpl->os,
528  "",
529  " Passed",
530  " Failed",
531  " Total");
532  printTotalResult(
533  *pimpl->os,
534  "Suites",
535  pimpl->statistics.getSuitesOK(),
536  pimpl->statistics.getSuitesFailed(),
537  pimpl->statistics.getSuites());
538  printTotalResult(
539  *pimpl->os,
540  "Cases",
541  pimpl->statistics.getCasesOK(),
542  pimpl->statistics.getCasesFailed(),
543  pimpl->statistics.getCases());
544  printTotalResult(
545  *pimpl->os,
546  "Checks",
547  pimpl->statistics.getAssertionsOK(),
548  pimpl->statistics.getAssertionsFailed(),
549  pimpl->statistics.getAssertions());
550  printTotalErrors(*pimpl->os, "Errors", pimpl->statistics.getErrors());
551  printResultLine(*pimpl->os, pimpl->term_driver, "Test total", result_, 0);
552  printHR(*pimpl->os, '=', "", 0);
553 }
554 
555 } /* namespace OTest2 */
OTest2::Style
Style
Style of shown text.
Definition: reporterattributes.h:44
OTest2::ReporterConsole::enterError
virtual AssertBufferPtr enterError(const Context &context_) override
Enter an error report.
Definition: reporterconsole.cpp:465
OTest2::ReporterConsole::enterAssert
virtual AssertBufferPtr enterAssert(const Context &context_, bool condition_, const std::string &file_, int lineno_) override
Enter an assertion.
Definition: reporterconsole.cpp:452
OTest2::AssertBufferListener::assertionClose
virtual void assertionClose(const Context &context_, const AssertBufferAssertData &data_)=0
Closing of the assertion.
OTest2::ReporterConsole::operator=
ReporterConsole & operator=(const ReporterConsole &)=delete
OTest2::AssertBufferStr::setBackground
virtual void setBackground(Color color_) override
Set background color.
Definition: assertbufferstr.cpp:84
OTest2::Style::BOLD
@ BOLD
OTest2::AssertBufferStr::setForeground
virtual void setForeground(Color color_) override
Set foreground color.
Definition: assertbufferstr.cpp:79
OTest2::AssertBufferStrPtr
std::shared_ptr< AssertBufferStr > AssertBufferStrPtr
Definition: assertbufferstrptr.h:27
utils.h
OTest2::Color::GREEN
@ GREEN
OTest2::ReporterConsole::~ReporterConsole
virtual ~ReporterConsole()
Dtor.
Definition: reporterconsole.cpp:409
terminaldriver.h
OTest2::Parameters::mixWithName
std::string mixWithName(const std::string &name_) const
Create one string mixed with a name of a testing object.
Definition: parameters.cpp:80
OTest2::Color::RED
@ RED
OTest2::Parameters
Generic parameters of a run of an testing object.
Definition: parameters.h:30
objectpath.h
reporterstatistics.h
OTest2::Color
Color
List of colors supported by the reporters.
Definition: reporterattributes.h:30
OTest2
Definition: assertbean.h:25
OTest2::AssertBufferStr::setTextStyle
virtual void setTextStyle(Style style_) override
Set style of the shown text.
Definition: assertbufferstr.cpp:89
OTest2::ReporterConsole::enterState
virtual void enterState(const Context &context_, const std::string &name_) override
Enter a state.
Definition: reporterconsole.cpp:446
OTest2::AssertBufferListener::assertionAdditionalMessage
virtual void assertionAdditionalMessage(const Context &context_, const AssertBufferAssertData &data_, const std::string &message_)=0
Additional assertion message.
OTest2::AssertBufferListener::errorClose
virtual void errorClose(const Context &context_)=0
Closing of the internal error.
reporterconsole.h
parameters.h
OTest2::AssertBufferListener::errorAdditionalMessage
virtual void errorAdditionalMessage(const Context &context_, const std::string &message_)=0
Additional error message.
OTest2::ReporterConsole::ReporterConsole
ReporterConsole(const ReporterConsole &)=delete
OTest2::ReporterConsole::leaveTest
virtual void leaveTest(const Context &context_, const std::string &name_, const Parameters &params_, bool result_) override
Leave entire test.
Definition: reporterconsole.cpp:519
OTest2::AssertBufferStr::resetAttributes
virtual void resetAttributes() override
Reset currently set text attributes.
Definition: assertbufferstr.cpp:94
OTest2::ReporterConsole::leaveCase
virtual void leaveCase(const Context &context_, const std::string &name_, const Parameters &params_, bool result_) override
Leave a case.
Definition: reporterconsole.cpp:482
OTest2::AssertBufferListener::assertionOpeningMessage
virtual void assertionOpeningMessage(const Context &context_, const AssertBufferAssertData &data_, const std::string &message_)=0
First composed message of an assertion.
context.h
OTest2::AssertBuffer::AssertBuffer
AssertBuffer(const AssertBuffer &)=delete
OTest2::Context
OTest2 runtime context.
Definition: context.h:38
OTest2::AssertBufferPtr
std::shared_ptr< AssertBuffer > AssertBufferPtr
Definition: assertbufferptr.h:27
OTest2::ReporterConsole::enterCase
virtual void enterCase(const Context &context_, const std::string &name_, const Parameters &params_) override
Enter a case.
Definition: reporterconsole.cpp:433
OTest2::odelete
void odelete(T_ *&object_)
Delete a pointer and set it invalid.
Definition: utils.h:34
assertbufferstr.h
OTest2::AssertBufferListener::errorOpeningMessage
virtual void errorOpeningMessage(const Context &context_, const std::string &message_)=0
First composed message of an internal error.
OTest2::ReporterConsole::leaveSuite
virtual void leaveSuite(const Context &context_, const std::string &name_, const Parameters &params_, bool result_) override
Leave a suite.
Definition: reporterconsole.cpp:499
OTest2::ReporterConsole::enterSuite
virtual void enterSuite(const Context &context_, const std::string &name_, const Parameters &params_) override
Enter a suite.
Definition: reporterconsole.cpp:420
OTest2::ReporterConsole::enterTest
virtual void enterTest(const Context &context_, const std::string &name_, const Parameters &params_) override
Enter entire test.
Definition: reporterconsole.cpp:413
OTest2::ReporterConsole::leaveState
virtual void leaveState(const Context &context_, const std::string &name_, bool result_) override
Leave a state.
Definition: reporterconsole.cpp:475