OTest2
A C++ testing framework
terminaldriver.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 
20 #include <terminaldriver.h>
21 
22 #include <assert.h>
23 #include <curses.h>
24 #include <fstream>
25 #include <iostream>
26 #include <memory>
27 #include <streambuf>
28 #include <string>
29 #include <term.h>
30 #include <unistd.h>
31 
32 #include <utils.h>
33 
34 namespace OTest2 {
35 
36 namespace {
37 
38 class Driver {
39  public:
40  /* -- avoid copying */
41  Driver(
42  const Driver&) = delete;
43  Driver& operator =(
44  const Driver&) = delete;
45 
46  Driver();
47  virtual ~Driver();
48 
49  virtual void setForeground(
50  std::streambuf& buffer_,
51  Color color_) = 0;
52  virtual void setBackground(
53  std::streambuf& buffer_,
54  Color color_) = 0;
55  virtual void setTextStyle(
56  std::streambuf& buffer_,
57  Style style_) = 0;
58  virtual void cleanAttributes(
59  std::streambuf& buffer_) = 0;
60 
61  protected:
62  static void printSequence(
63  std::streambuf& buffer_,
64  const char* sequence_);
65 };
66 
67 Driver::Driver() = default;
68 Driver::~Driver() = default;
69 
70 void Driver::printSequence(
71  std::streambuf& buffer_,
72  const char* sequence_) {
73  while(*sequence_ != 0) {
74  buffer_.sputc(*sequence_);
75  ++sequence_;
76  }
77 }
78 
79 class DriverNull : public Driver {
80  public:
81  /* -- avoid copying */
82  DriverNull(
83  const DriverNull&) = delete;
84  DriverNull& operator =(
85  const DriverNull&) = delete;
86 
87  DriverNull();
88  virtual ~DriverNull();
89 
90  virtual void setForeground(
91  std::streambuf& buffer_,
92  Color color_) override;
93  virtual void setBackground(
94  std::streambuf& buffer_,
95  Color color_) override;
96  virtual void setTextStyle(
97  std::streambuf& buffer_,
98  Style style_) override;
99  virtual void cleanAttributes(
100  std::streambuf& buffer_) override;
101 };
102 
103 DriverNull::DriverNull() = default;
104 DriverNull::~DriverNull() = default;
105 
106 void DriverNull::setForeground(
107  std::streambuf& buffer_,
108  Color color_) {
109  /* -- nothing to do */
110 }
111 
112 void DriverNull::setBackground(
113  std::streambuf& buffer_,
114  Color color_) {
115  /* -- nothing to do */
116 }
117 
118 void DriverNull::setTextStyle(
119  std::streambuf& buffer_,
120  Style style_) {
121  /* -- nothing to do */
122 }
123 
124 void DriverNull::cleanAttributes(
125  std::streambuf& buffer_) {
126  /* -- nothing to do */
127 }
128 
129 class DriverTerminfo : public Driver {
130  private:
131  const std::string sgr0;
132  const std::string setaf;
133  const std::string setf;
134  const std::string setab;
135  const std::string setb;
136  const std::string bold;
137  const std::string dim;
138 
139  static std::string initSequence(
140  const char* seq_id_);
141 
142  static int convertAnsiColor(
143  Color color_);
144  static int convertLegacyColor(
145  Color color_);
146 
147  public:
148  /* -- avoid copying */
149  DriverTerminfo(
150  const DriverTerminfo&) = delete;
151  DriverTerminfo& operator =(
152  const DriverTerminfo&) = delete;
153 
154  explicit DriverTerminfo();
155  virtual ~DriverTerminfo();
156 
157  virtual void setForeground(
158  std::streambuf& buffer_,
159  Color color_) override;
160  virtual void setBackground(
161  std::streambuf& buffer_,
162  Color color_) override;
163  virtual void setTextStyle(
164  std::streambuf& buffer_,
165  Style style_) override;
166  virtual void cleanAttributes(
167  std::streambuf& buffer_) override;
168 };
169 
170 std::string DriverTerminfo::initSequence(
171  const char* seq_id_) {
172  const char* sequence_(tigetstr(seq_id_));
173  if(sequence_ != nullptr)
174  return std::string(sequence_);
175  else
176  return std::string();
177 }
178 
179 int DriverTerminfo::convertAnsiColor(
180  Color color_) {
181  switch(color_) {
182  case Color::BLACK:
183  return 0;
184  case Color::RED:
185  return 1;
186  case Color::GREEN:
187  return 2;
188  case Color::YELLOW:
189  return 3;
190  case Color::BLUE:
191  return 4;
192  case Color::MAGENTA:
193  return 5;
194  case Color::CYAN:
195  return 6;
196  case Color::WHITE:
197  return 7;
198  default:
199  assert(false);
200  return 7;
201  }
202 }
203 
204 int DriverTerminfo::convertLegacyColor(
205  Color color_) {
206  switch(color_) {
207  case Color::BLACK:
208  return 0;
209  case Color::RED:
210  return 4;
211  case Color::GREEN:
212  return 2;
213  case Color::YELLOW:
214  return 6;
215  case Color::BLUE:
216  return 1;
217  case Color::MAGENTA:
218  return 5;
219  case Color::CYAN:
220  return 3;
221  case Color::WHITE:
222  return 7;
223  default:
224  assert(false);
225  return 7;
226  }
227 }
228 
229 DriverTerminfo::DriverTerminfo() :
230  sgr0(initSequence("sgr0")),
231  setaf(initSequence("setaf")),
232  setf(initSequence("setf")),
233  setab(initSequence("setab")),
234  setb(initSequence("setb")),
235  bold(initSequence("bold")),
236  dim(initSequence("dim")) {
237 
238 }
239 
240 DriverTerminfo::~DriverTerminfo() = default;
241 
242 void DriverTerminfo::setForeground(
243  std::streambuf& buffer_,
244  Color color_) {
245  /* -- don't set attributes if they cannot be reset */
246  if(sgr0.empty())
247  return;
248 
249  /* -- ANSI colors */
250  if(!setaf.empty()) {
251  printSequence(buffer_, tparm(setaf.c_str(), convertAnsiColor(color_)));
252  return;
253  }
254 
255  /* -- legacy colors */
256  if(!setf.empty()) {
257  printSequence(buffer_, tparm(setf.c_str(), convertLegacyColor(color_)));
258  return;
259  }
260 }
261 
262 void DriverTerminfo::setBackground(
263  std::streambuf& buffer_,
264  Color color_) {
265  /* -- don't set attributes if they cannot be reset */
266  if(sgr0.empty())
267  return;
268 
269  /* -- ANSI colors */
270  if(!setab.empty()) {
271  printSequence(buffer_, tparm(setab.c_str(), convertAnsiColor(color_)));
272  return;
273  }
274 
275  /* -- legacy colors */
276  if(!setb.empty()) {
277  printSequence(buffer_, tparm(setb.c_str(), convertLegacyColor(color_)));
278  return;
279  }
280 }
281 
282 void DriverTerminfo::setTextStyle(
283  std::streambuf& buffer_,
284  Style style_) {
285  /* -- don't set attributes if they cannot be reset */
286  if(sgr0.empty())
287  return;
288 
289  switch(style_) {
290  case Style::BOLD:
291  if(!bold.empty())
292  printSequence(buffer_, tparm(bold.c_str()));
293  break;
294 
295  case Style::DIM:
296  if(!dim.empty())
297  printSequence(buffer_, tparm(dim.c_str()));
298  break;
299 
300  default:
301  assert(false);
302  break;
303  }
304 }
305 
306 void DriverTerminfo::cleanAttributes(
307  std::streambuf& buffer_) {
308  if(!sgr0.empty()) {
309  printSequence(buffer_, tparm(sgr0.c_str()));
310  }
311 }
312 
313 } /* -- namespace */
314 
315 struct TerminalDriver::Impl {
316  public:
317  std::unique_ptr<Driver> driver;
318 
319  /* -- avoid copying */
320  Impl(
321  const Impl&) = delete;
322  Impl& operator =(
323  const Impl&) = delete;
324 
325  explicit Impl(
326  int handle_);
327  ~Impl();
328 };
329 
330 TerminalDriver::Impl::Impl(
331  int handle_) {
332  /* -- initialize the terminfo */
333  if(handle_ >= 0 && isatty(handle_)) {
334  int err_;
335  int info_(setupterm(nullptr, handle_, &err_));
336  if(info_ == OK && err_ == 1) {
337  driver = ::OTest2::make_unique<DriverTerminfo>();
338  return;
339  }
340  }
341 
342  /* -- In all other cases set the null driver up. */
343  driver = ::OTest2::make_unique<DriverNull>();
344 }
345 
346 TerminalDriver::Impl::~Impl() {
347 
348 }
349 
351  int handle_) :
352  pimpl(new Impl(handle_)) {
353 
354 }
355 
357  odelete(pimpl);
358 }
359 
361  std::streambuf& buffer_,
362  Color color_) {
363  pimpl->driver->setForeground(buffer_, color_);
364 }
365 
367  std::streambuf& buffer_,
368  Color color_) {
369  pimpl->driver->setBackground(buffer_, color_);
370 }
371 
373  std::streambuf& buffer_,
374  Style style_) {
375  pimpl->driver->setTextStyle(buffer_, style_);
376 }
377 
379  std::streambuf& buffer_) {
380  pimpl->driver->cleanAttributes(buffer_);
381 }
382 
383 } /* namespace OTest2 */
OTest2::Color::BLACK
@ BLACK
OTest2::Style::DIM
@ DIM
OTest2::Style
Style
Style of shown text.
Definition: reporterattributes.h:44
OTest2::Color::YELLOW
@ YELLOW
OTest2::TerminalDriver::cleanAttributes
void cleanAttributes(std::streambuf &buffer_)
Clean all previously set terminal attributes.
Definition: terminaldriver.cpp:378
OTest2::Style::BOLD
@ BOLD
utils.h
OTest2::Color::GREEN
@ GREEN
OTest2::TerminalDriver::setTextStyle
void setTextStyle(std::streambuf &buffer_, Style style_)
Change the text style.
Definition: terminaldriver.cpp:372
terminaldriver.h
OTest2::Color::MAGENTA
@ MAGENTA
OTest2::TerminalDriver::~TerminalDriver
~TerminalDriver()
Dtor.
Definition: terminaldriver.cpp:356
OTest2::Color::RED
@ RED
OTest2::TerminalDriver::TerminalDriver
TerminalDriver(const TerminalDriver &)=delete
OTest2::TerminalDriver::operator=
TerminalDriver & operator=(const TerminalDriver &)=delete
OTest2::Color
Color
List of colors supported by the reporters.
Definition: reporterattributes.h:30
OTest2
Definition: assertbean.h:25
OTest2::TerminalDriver::setBackground
void setBackground(std::streambuf &buffer_, Color color_)
Set background color.
Definition: terminaldriver.cpp:366
OTest2::Color::CYAN
@ CYAN
OTest2::Color::BLUE
@ BLUE
OTest2::Color::WHITE
@ WHITE
OTest2::TerminalDriver::setForeground
void setForeground(std::streambuf &buffer_, Color color_)
Change the foreground color of the text.
Definition: terminaldriver.cpp:360
OTest2::odelete
void odelete(T_ *&object_)
Delete a pointer and set it invalid.
Definition: utils.h:34