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