Line data Source code
1 : /* Classes for styling text cells (color, URLs).
2 : Copyright (C) 2023-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "config.h"
22 : #define INCLUDE_ALGORITHM
23 : #define INCLUDE_VECTOR
24 : #include "system.h"
25 : #include "coretypes.h"
26 : #include "pretty-print.h"
27 : #include "intl.h"
28 : #include "selftest.h"
29 : #include "text-art/selftests.h"
30 : #include "text-art/types.h"
31 : #include "color-macros.h"
32 : #include "diagnostics/color.h"
33 :
34 : using namespace text_art;
35 :
36 : /* class text_art::style. */
37 :
38 : style &
39 24 : style::set_style_url (const char *url)
40 : {
41 24 : m_url.clear ();
42 672 : while (*url)
43 648 : m_url.push_back (*(url++));
44 24 : return *this;
45 : }
46 :
47 : /* class text_art::style::color. */
48 :
49 : bool
50 10222 : style::color::operator== (const style::color &other) const
51 : {
52 10222 : if (m_kind != other.m_kind)
53 : return false;
54 9254 : switch (m_kind)
55 : {
56 0 : default:
57 0 : gcc_unreachable ();
58 7904 : case kind::NAMED:
59 7904 : return (u.m_named.m_name == other.u.m_named.m_name
60 7904 : && u.m_named.m_bright == other.u.m_named.m_bright);
61 8 : case kind::BITS_8:
62 8 : return u.m_8bit == other.u.m_8bit;
63 1342 : case kind::BITS_24:
64 1342 : return (u.m_24bit.r == other.u.m_24bit.r
65 686 : && u.m_24bit.g == other.u.m_24bit.g
66 2028 : && u.m_24bit.b == other.u.m_24bit.b);
67 : }
68 : }
69 :
70 : static void
71 516 : ensure_separator (pretty_printer *pp, bool &need_separator)
72 : {
73 0 : if (need_separator)
74 112 : pp_string (pp, COLOR_SEPARATOR);
75 516 : need_separator = true;
76 0 : }
77 :
78 : void
79 1056 : style::color::print_sgr (pretty_printer *pp,
80 : bool fg,
81 : bool &need_separator) const
82 : {
83 1056 : switch (m_kind)
84 : {
85 0 : default:
86 0 : gcc_unreachable ();
87 808 : case kind::NAMED:
88 808 : {
89 808 : static const char * const fg_normal[] = {"", // reset, for DEFAULT
90 : COLOR_FG_BLACK,
91 : COLOR_FG_RED,
92 : COLOR_FG_GREEN,
93 : COLOR_FG_YELLOW,
94 : COLOR_FG_BLUE,
95 : COLOR_FG_MAGENTA,
96 : COLOR_FG_CYAN,
97 : COLOR_FG_WHITE};
98 808 : static const char * const fg_bright[] = {"", // reset, for DEFAULT
99 : COLOR_FG_BRIGHT_BLACK,
100 : COLOR_FG_BRIGHT_RED,
101 : COLOR_FG_BRIGHT_GREEN,
102 : COLOR_FG_BRIGHT_YELLOW,
103 : COLOR_FG_BRIGHT_BLUE,
104 : COLOR_FG_BRIGHT_MAGENTA,
105 : COLOR_FG_BRIGHT_CYAN,
106 : COLOR_FG_BRIGHT_WHITE};
107 808 : static const char * const bg_normal[] = {"", // reset, for DEFAULT
108 : COLOR_BG_BLACK,
109 : COLOR_BG_RED,
110 : COLOR_BG_GREEN,
111 : COLOR_BG_YELLOW,
112 : COLOR_BG_BLUE,
113 : COLOR_BG_MAGENTA,
114 : COLOR_BG_CYAN,
115 : COLOR_BG_WHITE};
116 808 : static const char * const bg_bright[] = {"", // reset, for DEFAULT
117 : COLOR_BG_BRIGHT_BLACK,
118 : COLOR_BG_BRIGHT_RED,
119 : COLOR_BG_BRIGHT_GREEN,
120 : COLOR_BG_BRIGHT_YELLOW,
121 : COLOR_BG_BRIGHT_BLUE,
122 : COLOR_BG_BRIGHT_MAGENTA,
123 : COLOR_BG_BRIGHT_CYAN,
124 : COLOR_BG_BRIGHT_WHITE};
125 808 : STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (fg_bright));
126 808 : STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_normal));
127 808 : STATIC_ASSERT (ARRAY_SIZE (fg_normal) == ARRAY_SIZE (bg_bright));
128 808 : gcc_assert ((size_t)u.m_named.m_name < ARRAY_SIZE (fg_normal));
129 808 : const char *const *arr;
130 808 : if (fg)
131 436 : arr = u.m_named.m_bright ? fg_bright : fg_normal;
132 : else
133 372 : arr = u.m_named.m_bright ? bg_bright : bg_normal;
134 808 : const char *str = arr[(size_t)u.m_named.m_name];
135 808 : if (strlen (str) > 0)
136 : {
137 236 : ensure_separator (pp, need_separator);
138 236 : pp_string (pp, str);
139 : }
140 : }
141 : break;
142 48 : case kind::BITS_8:
143 48 : {
144 48 : ensure_separator (pp, need_separator);
145 48 : if (fg)
146 24 : pp_string (pp, "38");
147 : else
148 24 : pp_string (pp, "48");
149 48 : pp_printf (pp, ";5;%i", (int)u.m_8bit);
150 : }
151 48 : break;
152 200 : case kind::BITS_24:
153 200 : {
154 200 : ensure_separator (pp, need_separator);
155 200 : if (fg)
156 68 : pp_string (pp, "38");
157 : else
158 132 : pp_string (pp, "48");
159 200 : pp_printf (pp, ";2;%i;%i;%i",
160 200 : (int)u.m_24bit.r,
161 200 : (int)u.m_24bit.g,
162 200 : (int)u.m_24bit.b);
163 : }
164 200 : break;
165 : }
166 1056 : }
167 :
168 : /* class text_art::style. */
169 :
170 : /* See https://www.ecma-international.org/wp-content/uploads/ECMA-48_5th_edition_june_1991.pdf
171 : GRCM - GRAPHIC RENDITION COMBINATION MODE can be "REPLACING" or
172 : "CUMULATIVE", which affects whether we need to respecify all attributes
173 : at each SGR, or can accumulate them. Looks like we can't rely on the value
174 : of this, so we have to emit a single SGR for all changes, with a "0" reset
175 : at the front, forcing it to be effectively replacing. */
176 :
177 : void
178 5050 : style::print_changes (pretty_printer *pp,
179 : const style &old_style,
180 : const style &new_style)
181 : {
182 5050 : if (pp_show_color (pp))
183 : {
184 568 : bool needs_sgr = ((old_style.m_bold != new_style.m_bold)
185 520 : || (old_style.m_underscore != new_style.m_underscore)
186 512 : || (old_style.m_blink != new_style.m_blink)
187 504 : || (old_style.m_fg_color != new_style.m_fg_color)
188 832 : || (old_style.m_bg_color != new_style.m_bg_color));
189 528 : if (needs_sgr)
190 : {
191 1056 : bool emit_reset = (old_style.m_bold
192 504 : || new_style.m_bold
193 480 : || old_style.m_underscore
194 476 : || new_style.m_underscore
195 472 : || old_style.m_blink
196 996 : || new_style.m_blink);
197 528 : bool need_separator = false;
198 :
199 528 : pp_string (pp, SGR_START);
200 528 : if (emit_reset)
201 : {
202 64 : pp_string (pp, COLOR_NONE);
203 64 : need_separator = true;
204 : }
205 528 : if (new_style.m_bold)
206 : {
207 24 : gcc_assert (emit_reset);
208 24 : ensure_separator (pp, need_separator);
209 24 : pp_string (pp, COLOR_BOLD);
210 : }
211 528 : if (new_style.m_underscore)
212 : {
213 4 : gcc_assert (emit_reset);
214 4 : ensure_separator (pp, need_separator);
215 4 : pp_string (pp, COLOR_UNDERSCORE);
216 : }
217 528 : if (new_style.m_blink)
218 : {
219 4 : gcc_assert (emit_reset);
220 4 : ensure_separator (pp, need_separator);
221 4 : pp_string (pp, COLOR_BLINK);
222 : }
223 528 : new_style.m_fg_color.print_sgr (pp, true, need_separator);
224 528 : new_style.m_bg_color.print_sgr (pp, false, need_separator);
225 528 : pp_string (pp, SGR_END);
226 : }
227 : }
228 :
229 5050 : if (old_style.m_url != new_style.m_url)
230 : {
231 48 : if (!old_style.m_url.empty ())
232 24 : pp_end_url (pp);
233 48 : if (pp->supports_urls_p ()
234 48 : && !new_style.m_url.empty ())
235 : {
236 : /* Adapted from pp_begin_url, but encoding the
237 : chars to UTF-8 on the fly, rather than converting
238 : to a buffer. */
239 16 : pp_string (pp, "\33]8;;");
240 448 : for (auto ch : new_style.m_url)
241 432 : pp_unicode_character (pp, ch);
242 16 : switch (pp->get_url_format ())
243 : {
244 0 : default:
245 0 : case URL_FORMAT_NONE:
246 0 : gcc_unreachable ();
247 8 : case URL_FORMAT_ST:
248 8 : pp_string (pp, "\33\\");
249 8 : break;
250 8 : case URL_FORMAT_BEL:
251 8 : pp_string (pp, "\a");
252 8 : break;
253 : }
254 : }
255 : }
256 5050 : }
257 :
258 : /* Look up the current SGR codes for a color capability NAME
259 : (from GCC_COLORS or the defaults), and convert them to
260 : a text_art::style. */
261 :
262 : style
263 148 : text_art::get_style_from_color_cap_name (const char *name)
264 : {
265 148 : const char *sgr_codes = colorize_start (true, name);
266 148 : gcc_assert (sgr_codes);
267 :
268 : /* Parse the sgr codes. We expect the resulting styled_string to be
269 : empty; we're interested in the final style created during parsing. */
270 148 : style_manager sm;
271 148 : styled_string styled_str (sm, sgr_codes);
272 148 : return sm.get_style (sm.get_num_styles () - 1);
273 148 : }
274 :
275 : /* class text_art::style_manager. */
276 :
277 9318 : style_manager::style_manager ()
278 : {
279 : // index 0 will be the default style
280 9318 : m_styles.push_back (style ());
281 9318 : }
282 :
283 : style::id_t
284 2188 : style_manager::get_or_create_id (const style &s)
285 : {
286 : // For now, linear search
287 2188 : std::vector<style>::iterator existing
288 2188 : (std::find (m_styles.begin (), m_styles.end (), s));
289 :
290 : /* If found, return index of slot. */
291 2188 : if (existing != m_styles.end ())
292 1381 : return std::distance (m_styles.begin (), existing);
293 :
294 : /* Not found. */
295 :
296 : /* styled_str uses 7 bits for style information, so we can only support
297 : up to 128 different style combinations.
298 : Gracefully fail by turning off styling when this limit is reached. */
299 807 : if (m_styles.size () >= 127)
300 : return 0;
301 :
302 807 : m_styles.push_back (s);
303 807 : return m_styles.size () - 1;
304 : }
305 :
306 : void
307 7019 : style_manager::print_any_style_changes (pretty_printer *pp,
308 : style::id_t old_id,
309 : style::id_t new_id) const
310 : {
311 7019 : gcc_assert (pp);
312 7019 : if (old_id == new_id)
313 : return;
314 :
315 4826 : const style &old_style = m_styles[old_id];
316 4826 : const style &new_style = m_styles[new_id];
317 4826 : gcc_assert (!(old_style == new_style));
318 4826 : style::print_changes (pp, old_style, new_style);
319 : }
320 :
321 : #if CHECKING_P
322 :
323 : namespace selftest {
324 :
325 : void
326 224 : assert_style_change_streq (const location &loc,
327 : const style &old_style,
328 : const style &new_style,
329 : const char *expected_str)
330 : {
331 224 : pretty_printer pp;
332 224 : pp_show_color (&pp) = true;
333 224 : style::print_changes (&pp, old_style, new_style);
334 224 : ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_str);
335 224 : }
336 :
337 : #define ASSERT_STYLE_CHANGE_STREQ(OLD_STYLE, NEW_STYLE, EXPECTED_STR) \
338 : SELFTEST_BEGIN_STMT \
339 : assert_style_change_streq ((SELFTEST_LOCATION), \
340 : (OLD_STYLE), \
341 : (NEW_STYLE), \
342 : (EXPECTED_STR)); \
343 : SELFTEST_END_STMT
344 :
345 : static void
346 4 : test_bold ()
347 : {
348 4 : style_manager sm;
349 4 : ASSERT_EQ (sm.get_num_styles (), 1);
350 :
351 4 : style plain;
352 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
353 12 : ASSERT_EQ (sm.get_num_styles (), 1);
354 :
355 4 : style bold;
356 4 : bold.m_bold = true;
357 :
358 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
359 4 : ASSERT_EQ (sm.get_num_styles (), 2);
360 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
361 4 : ASSERT_EQ (sm.get_num_styles (), 2);
362 :
363 4 : ASSERT_STYLE_CHANGE_STREQ (plain, bold, "\33[00;01m\33[K");
364 4 : ASSERT_STYLE_CHANGE_STREQ (bold, plain, "\33[00m\33[K");
365 4 : }
366 :
367 : static void
368 4 : test_underscore ()
369 : {
370 4 : style_manager sm;
371 4 : ASSERT_EQ (sm.get_num_styles (), 1);
372 :
373 4 : style plain;
374 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
375 4 : ASSERT_EQ (sm.get_num_styles (), 1);
376 :
377 4 : style underscore;
378 4 : underscore.m_underscore = true;
379 :
380 4 : ASSERT_EQ (sm.get_or_create_id (underscore), 1);
381 4 : ASSERT_EQ (sm.get_num_styles (), 2);
382 4 : ASSERT_EQ (sm.get_or_create_id (underscore), 1);
383 4 : ASSERT_EQ (sm.get_num_styles (), 2);
384 :
385 4 : ASSERT_STYLE_CHANGE_STREQ (plain, underscore, "\33[00;04m\33[K");
386 4 : ASSERT_STYLE_CHANGE_STREQ (underscore, plain, "\33[00m\33[K");
387 4 : }
388 :
389 : static void
390 4 : test_blink ()
391 : {
392 4 : style_manager sm;
393 4 : ASSERT_EQ (sm.get_num_styles (), 1);
394 :
395 4 : style plain;
396 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
397 4 : ASSERT_EQ (sm.get_num_styles (), 1);
398 :
399 4 : style blink;
400 4 : blink.m_blink = true;
401 :
402 4 : ASSERT_EQ (sm.get_or_create_id (blink), 1);
403 4 : ASSERT_EQ (sm.get_num_styles (), 2);
404 4 : ASSERT_EQ (sm.get_or_create_id (blink), 1);
405 4 : ASSERT_EQ (sm.get_num_styles (), 2);
406 :
407 4 : ASSERT_STYLE_CHANGE_STREQ (plain, blink, "\33[00;05m\33[K");
408 4 : ASSERT_STYLE_CHANGE_STREQ (blink, plain, "\33[00m\33[K");
409 4 : }
410 :
411 : #define ASSERT_NAMED_COL_STREQ(NAMED_COLOR, FG, BRIGHT, EXPECTED_STR) \
412 : SELFTEST_BEGIN_STMT \
413 : { \
414 : style plain; \
415 : style s; \
416 : if (FG) \
417 : s.m_fg_color = style::color ((NAMED_COLOR), (BRIGHT)); \
418 : else \
419 : s.m_bg_color = style::color ((NAMED_COLOR), (BRIGHT)); \
420 : assert_style_change_streq ((SELFTEST_LOCATION), \
421 : plain, \
422 : s, \
423 : (EXPECTED_STR)); \
424 : } \
425 : SELFTEST_END_STMT
426 :
427 : static void
428 4 : test_named_colors ()
429 : {
430 : /* Foreground colors. */
431 4 : {
432 4 : const bool fg = true;
433 4 : {
434 4 : const bool bright = false;
435 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
436 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
437 : "[30m[K");
438 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
439 : "[31m[K");
440 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
441 : "[32m[K");
442 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
443 : "[33m[K");
444 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
445 : "[34m[K");
446 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
447 : "[35m[K");
448 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
449 : "[36m[K");
450 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
451 : "[37m[K");
452 : }
453 4 : {
454 4 : const bool bright = true;
455 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
456 : "[m[K");
457 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
458 : "[90m[K");
459 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
460 : "[91m[K");
461 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
462 : "[92m[K");
463 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
464 : "[93m[K");
465 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
466 : "[94m[K");
467 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
468 : "[95m[K");
469 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
470 : "[96m[K");
471 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
472 : "[97m[K");
473 : }
474 : }
475 :
476 : /* Background colors. */
477 4 : {
478 4 : const bool fg = false;
479 4 : {
480 4 : const bool bright = false;
481 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright, "");
482 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
483 : "[40m[K");
484 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
485 : "[41m[K");
486 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
487 : "[42m[K");
488 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
489 : "[43m[K");
490 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
491 : "[44m[K");
492 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
493 : "[45m[K");
494 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
495 : "[46m[K");
496 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
497 : "[47m[K");
498 : }
499 4 : {
500 4 : const bool bright = true;
501 4 : ASSERT_NAMED_COL_STREQ (style::named_color::DEFAULT, fg, bright,
502 : "[m[K");
503 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLACK, fg, bright,
504 : "[100m[K");
505 4 : ASSERT_NAMED_COL_STREQ (style::named_color::RED, fg, bright,
506 : "[101m[K");
507 4 : ASSERT_NAMED_COL_STREQ (style::named_color::GREEN, fg, bright,
508 : "[102m[K");
509 4 : ASSERT_NAMED_COL_STREQ (style::named_color::YELLOW, fg, bright,
510 : "[103m[K");
511 4 : ASSERT_NAMED_COL_STREQ (style::named_color::BLUE, fg, bright,
512 : "[104m[K");
513 4 : ASSERT_NAMED_COL_STREQ (style::named_color::MAGENTA, fg, bright,
514 : "[105m[K");
515 4 : ASSERT_NAMED_COL_STREQ (style::named_color::CYAN, fg, bright,
516 : "[106m[K");
517 4 : ASSERT_NAMED_COL_STREQ (style::named_color::WHITE, fg, bright,
518 : "[107m[K");
519 : }
520 : }
521 4 : }
522 :
523 : #define ASSERT_8_BIT_COL_STREQ(COL_VAL, FG, EXPECTED_STR) \
524 : SELFTEST_BEGIN_STMT \
525 : { \
526 : style plain; \
527 : style s; \
528 : if (FG) \
529 : s.m_fg_color = style::color (COL_VAL); \
530 : else \
531 : s.m_bg_color = style::color (COL_VAL); \
532 : assert_style_change_streq ((SELFTEST_LOCATION), \
533 : plain, \
534 : s, \
535 : (EXPECTED_STR)); \
536 : } \
537 : SELFTEST_END_STMT
538 :
539 : static void
540 4 : test_8_bit_colors ()
541 : {
542 : /* Foreground colors. */
543 4 : {
544 4 : const bool fg = true;
545 : /* 0-15: standard and high-intensity standard colors. */
546 4 : ASSERT_8_BIT_COL_STREQ (0, fg, "[38;5;0m[K");
547 4 : ASSERT_8_BIT_COL_STREQ (15, fg, "[38;5;15m[K");
548 : /* 16-231: 6x6x6 color cube. */
549 4 : ASSERT_8_BIT_COL_STREQ (16, fg, "[38;5;16m[K");
550 4 : ASSERT_8_BIT_COL_STREQ (231, fg, "[38;5;231m[K");
551 : /* 232-255: grayscale. */
552 4 : ASSERT_8_BIT_COL_STREQ (232, fg, "[38;5;232m[K");
553 4 : ASSERT_8_BIT_COL_STREQ (255, fg, "[38;5;255m[K");
554 : }
555 : /* Background colors. */
556 4 : {
557 4 : const bool fg = false;
558 : /* 0-15: standard and high-intensity standard colors. */
559 4 : ASSERT_8_BIT_COL_STREQ (0, fg, "[48;5;0m[K");
560 4 : ASSERT_8_BIT_COL_STREQ (15, fg, "[48;5;15m[K");
561 : /* 16-231: 6x6x6 color cube. */
562 4 : ASSERT_8_BIT_COL_STREQ (16, fg, "[48;5;16m[K");
563 4 : ASSERT_8_BIT_COL_STREQ (231, fg, "[48;5;231m[K");
564 : /* 232-255: grayscale. */
565 4 : ASSERT_8_BIT_COL_STREQ (232, fg, "[48;5;232m[K");
566 4 : ASSERT_8_BIT_COL_STREQ (255, fg, "[48;5;255m[K");
567 : }
568 4 : }
569 :
570 : #define ASSERT_24_BIT_COL_STREQ(R, G, B, FG, EXPECTED_STR) \
571 : SELFTEST_BEGIN_STMT \
572 : { \
573 : style plain; \
574 : style s; \
575 : if (FG) \
576 : s.m_fg_color = style::color ((R), (G), (B)); \
577 : else \
578 : s.m_bg_color = style::color ((R), (G), (B)); \
579 : assert_style_change_streq ((SELFTEST_LOCATION), \
580 : plain, \
581 : s, \
582 : (EXPECTED_STR)); \
583 : } \
584 : SELFTEST_END_STMT
585 :
586 : static void
587 4 : test_24_bit_colors ()
588 : {
589 : /* Foreground colors. */
590 4 : {
591 4 : const bool fg = true;
592 : // #F3FAF2:
593 4 : ASSERT_24_BIT_COL_STREQ (0xf3, 0xfa, 0xf2, fg,
594 : "[38;2;243;250;242m[K");
595 : }
596 : /* Background colors. */
597 4 : {
598 4 : const bool fg = false;
599 : // #FDF7E7
600 4 : ASSERT_24_BIT_COL_STREQ (0xfd, 0xf7, 0xe7, fg,
601 : "[48;2;253;247;231m[K");
602 : }
603 4 : }
604 :
605 : static void
606 4 : test_style_combinations ()
607 : {
608 4 : style_manager sm;
609 4 : ASSERT_EQ (sm.get_num_styles (), 1);
610 :
611 4 : style plain;
612 4 : ASSERT_EQ (sm.get_or_create_id (plain), 0);
613 4 : ASSERT_EQ (sm.get_num_styles (), 1);
614 :
615 4 : style bold;
616 4 : bold.m_bold = true;
617 :
618 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
619 4 : ASSERT_EQ (sm.get_num_styles (), 2);
620 4 : ASSERT_EQ (sm.get_or_create_id (bold), 1);
621 4 : ASSERT_EQ (sm.get_num_styles (), 2);
622 :
623 4 : style magenta_on_blue;
624 4 : magenta_on_blue.m_fg_color = style::named_color::MAGENTA;
625 4 : magenta_on_blue.m_bg_color = style::named_color::BLUE;
626 4 : ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
627 4 : ASSERT_EQ (sm.get_num_styles (), 3);
628 4 : ASSERT_EQ (sm.get_or_create_id (magenta_on_blue), 2);
629 4 : ASSERT_EQ (sm.get_num_styles (), 3);
630 4 : }
631 :
632 : /* Run all selftests in this file. */
633 :
634 : void
635 4 : text_art_style_cc_tests ()
636 : {
637 4 : test_bold ();
638 4 : test_underscore ();
639 4 : test_blink ();
640 4 : test_named_colors ();
641 4 : test_8_bit_colors ();
642 4 : test_24_bit_colors ();
643 4 : test_style_combinations ();
644 4 : }
645 :
646 : } // namespace selftest
647 :
648 :
649 : #endif /* #if CHECKING_P */
|