hppa: Fix LO_SUM DLTIND14R address support in PRINT_OPERAND_ADDRESS
[official-gcc.git] / gcc / text-art / table.cc
blob53f7126abce19753535aa0a13e690f6e37369599
1 /* Support for tabular/grid-based content.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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.
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.
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/>. */
21 #include "config.h"
22 #define INCLUDE_MEMORY
23 #define INCLUDE_VECTOR
24 #include "system.h"
25 #include "coretypes.h"
26 #include "make-unique.h"
27 #include "pretty-print.h"
28 #include "diagnostic.h"
29 #include "selftest.h"
30 #include "text-art/selftests.h"
31 #include "text-art/table.h"
33 using namespace text_art;
35 /* class text_art::table_cell_content. */
37 table_cell_content::table_cell_content (styled_string &&s)
38 : m_str (std::move (s)),
39 /* We assume here that the content occupies a single canvas row. */
40 m_size (m_str.calc_canvas_width (), 1)
44 void
45 table_cell_content::paint_to_canvas (canvas &canvas,
46 canvas::coord_t top_left) const
48 canvas.paint_text (top_left, m_str);
51 /* struct text_art::table_dimension_sizes. */
53 table_dimension_sizes::table_dimension_sizes (unsigned num)
54 : m_requirements (num, 0)
58 /* class text_art::table::cell_placement. */
60 void
61 table::cell_placement::paint_cell_contents_to_canvas(canvas &canvas,
62 canvas::coord_t offset,
63 const table_geometry &tg) const
65 const canvas::size_t req_canvas_size = get_min_canvas_size ();
66 const canvas::size_t alloc_canvas_size = tg.get_canvas_size (m_rect);
67 gcc_assert (req_canvas_size.w <= alloc_canvas_size.w);
68 gcc_assert (req_canvas_size.h <= alloc_canvas_size.h);
69 const int x_padding = alloc_canvas_size.w - req_canvas_size.w;
70 const int y_padding = alloc_canvas_size.h - req_canvas_size.h;
71 const table::coord_t table_top_left = m_rect.m_top_left;
72 const canvas::coord_t canvas_top_left = tg.table_to_canvas (table_top_left);
74 gcc_assert (x_padding >= 0);
75 int x_align_offset;
76 switch (m_x_align)
78 default:
79 gcc_unreachable ();
80 case x_align::LEFT:
81 x_align_offset = 0;
82 break;
83 case x_align::CENTER:
84 x_align_offset = x_padding / 2;
85 break;
86 case x_align::RIGHT:
87 x_align_offset = x_padding;
88 break;
91 gcc_assert (y_padding >= 0);
92 int y_align_offset;
93 switch (m_y_align)
95 default:
96 gcc_unreachable ();
97 case y_align::TOP:
98 y_align_offset = 0;
99 break;
100 case y_align::CENTER:
101 y_align_offset = y_padding / 2;
102 break;
103 case y_align::BOTTOM:
104 y_align_offset = y_padding;
105 break;
107 const canvas::coord_t content_rel_coord
108 (canvas_top_left.x + 1 + x_align_offset,
109 canvas_top_left.y + 1 + y_align_offset);
110 m_content.paint_to_canvas (canvas, offset + content_rel_coord);
113 /* class text_art::table. */
116 table::table (size_t size)
117 : m_size (size),
118 m_placements (),
119 m_occupancy (size)
121 m_occupancy.fill (-1);
124 void
125 table::set_cell (coord_t coord,
126 table_cell_content &&content,
127 enum x_align x_align,
128 enum y_align y_align)
130 set_cell_span (rect_t (coord, table::size_t (1, 1)),
131 std::move (content), x_align, y_align);
134 void
135 table::set_cell_span (rect_t span,
136 table_cell_content &&content,
137 enum x_align x_align,
138 enum y_align y_align)
140 gcc_assert (span.m_size.w > 0);
141 gcc_assert (span.m_size.h > 0);
142 int placement_idx = m_placements.size ();
143 m_placements.emplace_back (cell_placement (span, std::move (content),
144 x_align, y_align));
145 for (int y = span.get_min_y (); y < span.get_next_y (); y++)
146 for (int x = span.get_min_x (); x < span.get_next_x (); x++)
148 gcc_assert (m_occupancy.get (coord_t (x, y)) == -1);
149 m_occupancy.set (coord_t (x, y), placement_idx);
153 /* If SPAN is unoccuped, set it to CONTENT.
154 Otherwise, discard CONTENT. */
156 void
157 table::maybe_set_cell_span (rect_t span,
158 table_cell_content &&content,
159 enum x_align x_align,
160 enum y_align y_align)
162 gcc_assert (span.m_size.w > 0);
163 gcc_assert (span.m_size.h > 0);
164 for (int y = span.get_min_y (); y < span.get_next_y (); y++)
165 for (int x = span.get_min_x (); x < span.get_next_x (); x++)
167 if (m_occupancy.get (coord_t (x, y)) != -1)
168 return;
170 set_cell_span (span, std::move (content), x_align, y_align);
173 canvas
174 table::to_canvas (const theme &theme, const style_manager &sm) const
176 table_dimension_sizes col_widths (m_size.w);
177 table_dimension_sizes row_heights (m_size.h);
178 table_cell_sizes cell_sizes (col_widths, row_heights);
179 cell_sizes.pass_1 (*this);
180 cell_sizes.pass_2 (*this);
181 table_geometry tg (*this, cell_sizes);
182 canvas canvas (tg.get_canvas_size (), sm);
183 paint_to_canvas (canvas, canvas::coord_t (0, 0), tg, theme);
184 return canvas;
187 void
188 table::paint_to_canvas (canvas &canvas,
189 canvas::coord_t offset,
190 const table_geometry &tg,
191 const theme &theme) const
193 canvas.fill (canvas::rect_t (offset, tg.get_canvas_size ()),
194 styled_unichar (' '));
195 paint_cell_borders_to_canvas (canvas, offset, tg, theme);
196 paint_cell_contents_to_canvas (canvas, offset, tg);
199 /* Print this table to stderr. */
201 DEBUG_FUNCTION void
202 table::debug () const
204 /* Use a temporary style manager.
205 Styles in the table will be meaningless, so
206 print the canvas with styling disabled. */
207 style_manager sm;
208 canvas canvas (to_canvas (unicode_theme (), sm));
209 canvas.debug (false);
212 /* Move OTHER's content this table, starting at OFFSET. */
214 void
215 table::add_other_table (table &&other,
216 table::coord_t offset)
218 for (auto &&placement : other.m_placements)
220 set_cell_span (placement.m_rect + offset,
221 std::move (placement.m_content),
222 placement.m_x_align,
223 placement.m_y_align);
227 const table::cell_placement *
228 table::get_placement_at (coord_t coord) const
230 const int placement_idx = m_occupancy.get (coord);
231 if (placement_idx == -1)
232 return nullptr;
233 return &m_placements[placement_idx];
237 table::get_occupancy_safe (coord_t coord) const
239 if (coord.x < 0)
240 return -1;
241 if (coord.x >= m_size.w)
242 return -1;
243 if (coord.y < 0)
244 return -1;
245 if (coord.y >= m_size.h)
246 return -1;
247 return m_occupancy.get (coord);
250 /* Determine if the "?" edges need borders for table cell D
251 in the following, for the directions relative to "X", based
252 on whether each of table cell boundaries AB, CD, AC, and BD
253 are boundaries between cell spans:
255 # up?
256 # +-----+-----+
257 # | |
258 # | ? |
259 # | A ? B |
260 # | ? |
261 # | |
262 # left?+ ??? X ??? + right?
263 # | |
264 # | ? |
265 # | C ? D |
266 # | ? |
267 # | |
268 # +-----+-----+
269 # down?
272 directions
273 table::get_connections (int table_x, int table_y) const
275 int cell_a = get_occupancy_safe (coord_t (table_x - 1, table_y - 1));
276 int cell_b = get_occupancy_safe (coord_t (table_x, table_y - 1));
277 int cell_c = get_occupancy_safe (coord_t (table_x - 1, table_y));
278 int cell_d = get_occupancy_safe (coord_t (table_x, table_y));
279 const bool up = (cell_a != cell_b);
280 const bool down = (cell_c != cell_d);
281 const bool left = (cell_a != cell_c);
282 const bool right = (cell_b != cell_d);
283 return directions (up, down, left, right);
286 /* Paint the grid lines.
288 Consider painting
289 - a grid of cells,
290 - plus a right-hand border
291 - and a bottom border
293 Then we need to paint to the canvas like this:
295 # PER-TABLE-COLUMN R BORDER
296 # +-------------------+ +-----+
298 # TABLE CELL WIDTH (in canvas units)
299 # +-------------+
300 # . . . . . . .
301 # ...+-----+-----+.+-----+...+-----+ +
302 # | U | |.| | | U | |
303 # | U | |.| | | U | |
304 # |LL+RR|RRRRR|.|RRRRR| |LL+ | |
305 # | D | |.| | | D | |
306 # | D | |.| | | D | |
307 # ...+-----+-----+.+-----+...+-----+ |
308 # ..................... ...... +-- PER-TABLE-ROW
309 # ...+-----+-----+.+-----+...+-----+ | +
310 # | D | |.| | | D | | |
311 # | D | |.| | | D | | |
312 # | D | |.| | | D | | +---- TABLE CELL HEIGHT (in canvas units)
313 # | D | |.| | | D | | |
314 # | D | |.| | | D | | |
315 # ...+-----+-----+.+-----+...+-----+ + +
316 # . . . . . .
317 # ...+-----+-----+.+-----+...+-----+ +
318 # | D | |.| | | U | |
319 # | D | |.| | | U | |
320 # |LL+RR|RRRRR|.|RRRRR| |LL+ | | BOTTOM BORDER
321 # | | |.| | | | |
322 # | | |.| | | | |
323 # ...+-----+-----+.+-----+...+-----+ +
325 where each:
327 # +-----+
328 # | |
329 # | |
330 # | |
331 # | |
332 # | |
333 # +-----+
335 is a canvas cell, and the U, L, R, D express the connections
336 that are present with neighboring table cells. These affect
337 the kinds of borders that we draw for a particular table cell. */
339 void
340 table::paint_cell_borders_to_canvas (canvas &canvas,
341 canvas::coord_t offset,
342 const table_geometry &tg,
343 const theme &theme) const
345 /* The per-table-cell left and top borders are either drawn or not,
346 but if they are, they aren't affected by per-table-cell connections. */
347 const canvas::cell_t left_border
348 = theme.get_line_art (directions (true, /* up */
349 true, /* down */
350 false, /* left */
351 false /* right */));
352 const canvas::cell_t top_border
353 = theme.get_line_art (directions (false, /* up */
354 false, /* down */
355 true, /* left */
356 true)); /* right */
357 for (int table_y = 0; table_y < m_size.h; table_y++)
359 const int canvas_y = tg.table_y_to_canvas_y (table_y);
360 for (int table_x = 0; table_x < m_size.w; table_x++)
362 canvas::coord_t canvas_top_left
363 = tg.table_to_canvas(table::coord_t (table_x, table_y));
365 const directions c (get_connections (table_x, table_y));
367 /* Paint top-left corner of border, if any. */
368 canvas.paint (offset + canvas_top_left,
369 theme.get_line_art (c));
371 /* Paint remainder of left border of cell, if any.
372 We assume here that the content occupies a single canvas row. */
373 if (c.m_down)
374 canvas.paint (offset + canvas::coord_t (canvas_top_left.x,
375 canvas_y + 1),
376 left_border);
378 /* Paint remainder of top border of cell, if any. */
379 if (c.m_right)
381 const int col_width = tg.get_col_width (table_x);
382 for (int x_offset = 0; x_offset < col_width; x_offset++)
384 const int canvas_x = canvas_top_left.x + 1 + x_offset;
385 canvas.paint (offset + canvas::coord_t (canvas_x, canvas_y),
386 top_border);
391 /* Paint right-hand border of row. */
392 const int table_x = m_size.w;
393 const int canvas_x = tg.table_x_to_canvas_x (table_x);
394 const directions c (get_connections (m_size.w, table_y));
395 canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y),
396 theme.get_line_art (directions (c.m_up,
397 c.m_down,
398 c.m_left,
399 false))); /* right */
400 /* We assume here that the content occupies a single canvas row. */
401 canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y + 1),
402 theme.get_line_art (directions (c.m_down, /* up */
403 c.m_down, /* down */
404 false, /* left */
405 false))); /* right */
408 /* Draw bottom border of table. */
410 const int canvas_y = tg.get_canvas_size ().h - 1;
411 for (int table_x = 0; table_x < m_size.w; table_x++)
413 const directions c (get_connections (table_x, m_size.h));
414 const int left_canvas_x = tg.table_x_to_canvas_x (table_x);
415 canvas.paint (offset + canvas::coord_t (left_canvas_x, canvas_y),
416 theme.get_line_art (directions (c.m_up,
417 false, /* down */
418 c.m_left,
419 c.m_right)));
420 const int col_width = tg.get_col_width (table_x);
421 for (int x_offset = 0; x_offset < col_width; x_offset++)
423 const int canvas_x = left_canvas_x + 1 + x_offset;
424 canvas.paint(offset + canvas::coord_t (canvas_x, canvas_y),
425 theme.get_line_art (directions (false, // up
426 false, // down
427 c.m_right, // left
428 c.m_right))); // right
432 /* Bottom-right corner of table. */
433 const int table_x = m_size.w;
434 const int canvas_x = tg.table_x_to_canvas_x (table_x);
435 const directions c (get_connections (m_size.w, m_size.h));
436 canvas.paint (offset + canvas::coord_t (canvas_x, canvas_y),
437 theme.get_line_art (directions (c.m_up, // up
438 false, // down
439 c.m_left, // left
440 false))); // right
444 void
445 table::paint_cell_contents_to_canvas(canvas &canvas,
446 canvas::coord_t offset,
447 const table_geometry &tg) const
449 for (auto &placement : m_placements)
450 placement.paint_cell_contents_to_canvas (canvas, offset, tg);
453 /* class table_cell_sizes. */
455 /* Consider 1x1 cells. */
457 void
458 table_cell_sizes::pass_1 (const table &table)
460 for (auto &placement : table.m_placements)
461 if (placement.one_by_one_p ())
463 canvas::size_t canvas_size (placement.get_min_canvas_size ());
464 table::coord_t table_coord (placement.m_rect.m_top_left);
465 m_col_widths.require (table_coord.x, canvas_size.w);
466 m_row_heights.require (table_coord.y, canvas_size.h);
470 /* Consider cells that span more than one row or column. */
472 void
473 table_cell_sizes::pass_2 (const table &table)
475 for (auto &placement : table.m_placements)
476 if (!placement.one_by_one_p ())
478 const canvas::size_t req_canvas_size (placement.get_min_canvas_size ());
479 const canvas::size_t current_canvas_size
480 = get_canvas_size (placement.m_rect);
481 /* Grow columns as necessary. */
482 if (req_canvas_size.w > current_canvas_size.w)
484 /* Spread the deficit amongst the columns. */
485 int deficit = req_canvas_size.w - current_canvas_size.w;
486 const int per_col = deficit / placement.m_rect.m_size.w;
487 for (int table_x = placement.m_rect.get_min_x ();
488 table_x < placement.m_rect.get_next_x ();
489 table_x++)
491 m_col_widths.m_requirements[table_x] += per_col;
492 deficit -= per_col;
494 /* Make sure we allocate all of the deficit. */
495 if (deficit > 0)
497 const int table_x = placement.m_rect.get_max_x ();
498 m_col_widths.m_requirements[table_x] += deficit;
501 /* Grow rows as necessary. */
502 if (req_canvas_size.h > current_canvas_size.h)
504 /* Spread the deficit amongst the rows. */
505 int deficit = req_canvas_size.h - current_canvas_size.h;
506 const int per_row = deficit / placement.m_rect.m_size.h;
507 for (int table_y = placement.m_rect.get_min_y ();
508 table_y < placement.m_rect.get_next_y ();
509 table_y++)
511 m_row_heights.m_requirements[table_y] += per_row;
512 deficit -= per_row;
514 /* Make sure we allocate all of the deficit. */
515 if (deficit > 0)
517 const int table_y = placement.m_rect.get_max_y ();
518 m_row_heights.m_requirements[table_y] += deficit;
524 canvas::size_t
525 table_cell_sizes::get_canvas_size (const table::rect_t &rect) const
527 canvas::size_t result (0, 0);
528 for (int table_x = rect.get_min_x ();
529 table_x < rect.get_next_x ();
530 table_x ++)
531 result.w += m_col_widths.m_requirements[table_x];
532 for (int table_y = rect.get_min_y ();
533 table_y < rect.get_next_y ();
534 table_y ++)
535 result.h += m_row_heights.m_requirements[table_y];
536 /* Allow space for the borders. */
537 result.w += rect.m_size.w - 1;
538 result.h += rect.m_size.h - 1;
539 return result;
542 /* class text_art::table_geometry. */
544 table_geometry::table_geometry (const table &table, table_cell_sizes &cell_sizes)
545 : m_cell_sizes (cell_sizes),
546 m_canvas_size (canvas::size_t (0, 0)),
547 m_col_start_x (table.get_size ().w),
548 m_row_start_y (table.get_size ().h)
550 recalc_coords ();
553 void
554 table_geometry::recalc_coords ()
556 /* Start canvas column of table cell, including leading border. */
557 m_col_start_x.clear ();
558 int iter_canvas_x = 0;
559 for (auto w : m_cell_sizes.m_col_widths.m_requirements)
561 m_col_start_x.push_back (iter_canvas_x);
562 iter_canvas_x += w + 1;
565 /* Start canvas row of table cell, including leading border. */
566 m_row_start_y.clear ();
567 int iter_canvas_y = 0;
568 for (auto h : m_cell_sizes.m_row_heights.m_requirements)
570 m_row_start_y.push_back (iter_canvas_y);
571 iter_canvas_y += h + 1;
574 m_canvas_size = canvas::size_t (iter_canvas_x + 1,
575 iter_canvas_y + 1);
578 /* Get the TL corner of the table cell at TABLE_COORD
579 in canvas coords (including the border). */
581 canvas::coord_t
582 table_geometry::table_to_canvas (table::coord_t table_coord) const
584 return canvas::coord_t (table_x_to_canvas_x (table_coord.x),
585 table_y_to_canvas_y (table_coord.y));
588 /* Get the left border of the table cell at column TABLE_X
589 in canvas coords (including the border). */
592 table_geometry::table_x_to_canvas_x (int table_x) const
594 /* Allow one beyond the end, for the right-hand border of the table. */
595 if (table_x == (int)m_col_start_x.size ())
596 return m_canvas_size.w - 1;
597 return m_col_start_x[table_x];
600 /* Get the top border of the table cell at column TABLE_Y
601 in canvas coords (including the border). */
604 table_geometry::table_y_to_canvas_y (int table_y) const
606 /* Allow one beyond the end, for the right-hand border of the table. */
607 if (table_y == (int)m_row_start_y.size ())
608 return m_canvas_size.h - 1;
609 return m_row_start_y[table_y];
612 /* class text_art::simple_table_geometry. */
614 simple_table_geometry::simple_table_geometry (const table &table)
615 : m_col_widths (table.get_size ().w),
616 m_row_heights (table.get_size ().h),
617 m_cell_sizes (m_col_widths, m_row_heights),
618 m_tg (table, m_cell_sizes)
620 m_cell_sizes.pass_1 (table);
621 m_cell_sizes.pass_2 (table);
622 m_tg.recalc_coords ();
625 #if CHECKING_P
627 namespace selftest {
629 static void
630 test_tic_tac_toe ()
632 style_manager sm;
633 table t (table::size_t (3, 3));
634 t.set_cell (table::coord_t (0, 0), styled_string (sm, "X"));
635 t.set_cell (table::coord_t (1, 0), styled_string (sm, ""));
636 t.set_cell (table::coord_t (2, 0), styled_string (sm, ""));
637 t.set_cell (table::coord_t (0, 1), styled_string (sm, "O"));
638 t.set_cell (table::coord_t (1, 1), styled_string (sm, "O"));
639 t.set_cell (table::coord_t (2, 1), styled_string (sm, ""));
640 t.set_cell (table::coord_t (0, 2), styled_string (sm, "X"));
641 t.set_cell (table::coord_t (1, 2), styled_string (sm, ""));
642 t.set_cell (table::coord_t (2, 2), styled_string (sm, "O"));
645 canvas canvas (t.to_canvas (ascii_theme (), sm));
646 ASSERT_CANVAS_STREQ
647 (canvas, false,
648 ("+-+-+-+\n"
649 "|X| | |\n"
650 "+-+-+-+\n"
651 "|O|O| |\n"
652 "+-+-+-+\n"
653 "|X| |O|\n"
654 "+-+-+-+\n"));
658 canvas canvas (t.to_canvas (unicode_theme (), sm));
659 ASSERT_CANVAS_STREQ
660 (canvas, false,
661 // FIXME: are we allowed unicode chars in string literals in our source?
662 ("┌─┬─┬─┐\n"
663 "│X│ │ │\n"
664 "├─┼─┼─┤\n"
665 "│O│O│ │\n"
666 "├─┼─┼─┤\n"
667 "│X│ │O│\n"
668 "└─┴─┴─┘\n"));
672 static table
673 make_text_table ()
675 style_manager sm;
676 table t (table::size_t (3, 3));
677 t.set_cell (table::coord_t (0, 0), styled_string (sm, "top left"));
678 t.set_cell (table::coord_t (1, 0), styled_string (sm, "top middle"));
679 t.set_cell (table::coord_t (2, 0), styled_string (sm, "top right"));
680 t.set_cell (table::coord_t (0, 1), styled_string (sm, "middle left"));
681 t.set_cell (table::coord_t (1, 1), styled_string (sm, "middle middle"));
682 t.set_cell (table::coord_t (2, 1), styled_string (sm, "middle right"));
683 t.set_cell (table::coord_t (0, 2), styled_string (sm, "bottom left"));
684 t.set_cell (table::coord_t (1, 2), styled_string (sm, "bottom middle"));
685 t.set_cell (table::coord_t (2, 2), styled_string (sm, "bottom right"));
686 return t;
689 static void
690 test_text_table ()
692 style_manager sm;
693 table table = make_text_table ();
695 canvas canvas (table.to_canvas (ascii_theme(), sm));
696 ASSERT_CANVAS_STREQ
697 (canvas, false,
698 ("+-----------+-------------+------------+\n"
699 "| top left | top middle | top right |\n"
700 "+-----------+-------------+------------+\n"
701 "|middle left|middle middle|middle right|\n"
702 "+-----------+-------------+------------+\n"
703 "|bottom left|bottom middle|bottom right|\n"
704 "+-----------+-------------+------------+\n"));
707 canvas canvas (table.to_canvas (unicode_theme(), sm));
708 ASSERT_CANVAS_STREQ
709 (canvas, false,
710 // FIXME: are we allowed unicode chars in string literals in our source?
711 ("┌───────────┬─────────────┬────────────┐\n"
712 "│ top left │ top middle │ top right │\n"
713 "├───────────┼─────────────┼────────────┤\n"
714 "│middle left│middle middle│middle right│\n"
715 "├───────────┼─────────────┼────────────┤\n"
716 "│bottom left│bottom middle│bottom right│\n"
717 "└───────────┴─────────────┴────────────┘\n"));
721 static void
722 test_offset_table ()
724 style_manager sm;
725 table table = make_text_table ();
726 simple_table_geometry stg (table);
727 const canvas::size_t tcs = stg.m_tg.get_canvas_size();
729 canvas canvas (canvas::size_t (tcs.w + 5, tcs.h + 5), sm);
730 canvas.debug_fill ();
731 table.paint_to_canvas (canvas, canvas::coord_t (3, 3),
732 stg.m_tg,
733 ascii_theme());
734 ASSERT_CANVAS_STREQ
735 (canvas, false,
736 ("*********************************************\n"
737 "*********************************************\n"
738 "*********************************************\n"
739 "***+-----------+-------------+------------+**\n"
740 "***| top left | top middle | top right |**\n"
741 "***+-----------+-------------+------------+**\n"
742 "***|middle left|middle middle|middle right|**\n"
743 "***+-----------+-------------+------------+**\n"
744 "***|bottom left|bottom middle|bottom right|**\n"
745 "***+-----------+-------------+------------+**\n"
746 "*********************************************\n"
747 "*********************************************\n"));
750 canvas canvas (canvas::size_t (tcs.w + 5, tcs.h + 5), sm);
751 canvas.debug_fill ();
752 table.paint_to_canvas (canvas, canvas::coord_t (3, 3),
753 stg.m_tg,
754 unicode_theme());
755 ASSERT_CANVAS_STREQ
756 (canvas, false,
757 // FIXME: are we allowed unicode chars in string literals in our source?
758 ("*********************************************\n"
759 "*********************************************\n"
760 "*********************************************\n"
761 "***┌───────────┬─────────────┬────────────┐**\n"
762 "***│ top left │ top middle │ top right │**\n"
763 "***├───────────┼─────────────┼────────────┤**\n"
764 "***│middle left│middle middle│middle right│**\n"
765 "***├───────────┼─────────────┼────────────┤**\n"
766 "***│bottom left│bottom middle│bottom right│**\n"
767 "***└───────────┴─────────────┴────────────┘**\n"
768 "*********************************************\n"
769 "*********************************************\n"));
773 #define ASSERT_TABLE_CELL_STREQ(TABLE, TABLE_X, TABLE_Y, EXPECTED_STR) \
774 SELFTEST_BEGIN_STMT \
775 table::coord_t coord ((TABLE_X), (TABLE_Y)); \
776 const table::cell_placement *cp = (TABLE).get_placement_at (coord); \
777 ASSERT_NE (cp, nullptr); \
778 ASSERT_EQ (cp->get_content (), styled_string (sm, EXPECTED_STR)); \
779 SELFTEST_END_STMT
781 #define ASSERT_TABLE_NULL_CELL(TABLE, TABLE_X, TABLE_Y) \
782 SELFTEST_BEGIN_STMT \
783 table::coord_t coord ((TABLE_X), (TABLE_Y)); \
784 const table::cell_placement *cp = (TABLE).get_placement_at (coord); \
785 ASSERT_EQ (cp, nullptr); \
786 SELFTEST_END_STMT
788 static void
789 test_spans ()
791 style_manager sm;
792 table table (table::size_t (3, 3));
793 table.set_cell_span (table::rect_t (table::coord_t (0, 0),
794 table::size_t (3, 1)),
795 styled_string (sm, "ABC"));
796 table.set_cell_span (table::rect_t (table::coord_t (0, 1),
797 table::size_t (2, 1)),
798 styled_string (sm, "DE"));
799 table.set_cell_span (table::rect_t (table::coord_t (2, 1),
800 table::size_t (1, 1)),
801 styled_string (sm, "F"));
802 table.set_cell (table::coord_t (0, 2), styled_string (sm, "G"));
803 table.set_cell (table::coord_t (1, 2), styled_string (sm, "H"));
804 table.set_cell (table::coord_t (2, 2), styled_string (sm, "I"));
806 canvas canvas (table.to_canvas (ascii_theme(), sm));
807 ASSERT_CANVAS_STREQ
808 (canvas, false,
809 ("+-----+\n"
810 "| ABC |\n"
811 "+---+-+\n"
812 "|DE |F|\n"
813 "+-+-+-+\n"
814 "|G|H|I|\n"
815 "+-+-+-+\n"));
818 canvas canvas (table.to_canvas (unicode_theme(), sm));
819 ASSERT_CANVAS_STREQ
820 (canvas, false,
821 // FIXME: are we allowed unicode chars in string literals in our source?
822 ("┌─────┐\n"
823 "│ ABC │\n"
824 "├───┬─┤\n"
825 "│DE │F│\n"
826 "├─┬─┼─┤\n"
827 "│G│H│I│\n"
828 "└─┴─┴─┘\n"));
832 /* Verify building this 5x5 table with spans:
833 |0|1|2|3|4|
834 +-+-+-+-+-+
835 0|A A A|B|C|0
836 + +-+ +
837 1|A A A|D|C|1
838 + +-+-+
839 2|A A A|E|F|2
840 +-+-+-+-+-+
841 3|G G|H|I I|3
842 | | +-+-+
843 4|G G|H|J J|4
844 +-+-+-+-+-+
845 |0|1|2|3|4|
848 static void
849 test_spans_2 ()
851 style_manager sm;
852 table table (table::size_t (5, 5));
853 table.set_cell_span (table::rect_t (table::coord_t (0, 0),
854 table::size_t (3, 3)),
855 styled_string (sm, "A"));
856 table.set_cell_span (table::rect_t (table::coord_t (3, 0),
857 table::size_t (1, 1)),
858 styled_string (sm, "B"));
859 table.set_cell_span (table::rect_t (table::coord_t (4, 0),
860 table::size_t (1, 2)),
861 styled_string (sm, "C"));
862 table.set_cell_span (table::rect_t (table::coord_t (3, 1),
863 table::size_t (1, 1)),
864 styled_string (sm, "D"));
865 table.set_cell_span (table::rect_t (table::coord_t (3, 2),
866 table::size_t (1, 1)),
867 styled_string (sm, "E"));
868 table.set_cell_span (table::rect_t (table::coord_t (4, 2),
869 table::size_t (1, 1)),
870 styled_string (sm, "F"));
871 table.set_cell_span (table::rect_t (table::coord_t (0, 3),
872 table::size_t (2, 2)),
873 styled_string (sm, "G"));
874 table.set_cell_span (table::rect_t (table::coord_t (2, 3),
875 table::size_t (1, 2)),
876 styled_string (sm, "H"));
877 table.set_cell_span (table::rect_t (table::coord_t (3, 3),
878 table::size_t (2, 1)),
879 styled_string (sm, "I"));
880 table.set_cell_span (table::rect_t (table::coord_t (3, 4),
881 table::size_t (2, 1)),
882 styled_string (sm, "J"));
884 /* Check occupancy at each table coordinate. */
885 ASSERT_TABLE_CELL_STREQ (table, 0, 0, "A");
886 ASSERT_TABLE_CELL_STREQ (table, 1, 0, "A");
887 ASSERT_TABLE_CELL_STREQ (table, 2, 0, "A");
888 ASSERT_TABLE_CELL_STREQ (table, 3, 0, "B");
889 ASSERT_TABLE_CELL_STREQ (table, 4, 0, "C");
891 ASSERT_TABLE_CELL_STREQ (table, 0, 1, "A");
892 ASSERT_TABLE_CELL_STREQ (table, 1, 1, "A");
893 ASSERT_TABLE_CELL_STREQ (table, 2, 1, "A");
894 ASSERT_TABLE_CELL_STREQ (table, 3, 1, "D");
895 ASSERT_TABLE_CELL_STREQ (table, 4, 1, "C");
897 ASSERT_TABLE_CELL_STREQ (table, 0, 2, "A");
898 ASSERT_TABLE_CELL_STREQ (table, 1, 2, "A");
899 ASSERT_TABLE_CELL_STREQ (table, 2, 2, "A");
900 ASSERT_TABLE_CELL_STREQ (table, 3, 2, "E");
901 ASSERT_TABLE_CELL_STREQ (table, 4, 2, "F");
903 ASSERT_TABLE_CELL_STREQ (table, 0, 3, "G");
904 ASSERT_TABLE_CELL_STREQ (table, 1, 3, "G");
905 ASSERT_TABLE_CELL_STREQ (table, 2, 3, "H");
906 ASSERT_TABLE_CELL_STREQ (table, 3, 3, "I");
907 ASSERT_TABLE_CELL_STREQ (table, 4, 3, "I");
909 ASSERT_TABLE_CELL_STREQ (table, 0, 4, "G");
910 ASSERT_TABLE_CELL_STREQ (table, 1, 4, "G");
911 ASSERT_TABLE_CELL_STREQ (table, 2, 4, "H");
912 ASSERT_TABLE_CELL_STREQ (table, 3, 4, "J");
913 ASSERT_TABLE_CELL_STREQ (table, 4, 4, "J");
916 canvas canvas (table.to_canvas (ascii_theme(), sm));
917 ASSERT_CANVAS_STREQ
918 (canvas, false,
919 ("+---+-+-+\n"
920 "| |B| |\n"
921 "| +-+C|\n"
922 "| A |D| |\n"
923 "| +-+-+\n"
924 "| |E|F|\n"
925 "+-+-+-+-+\n"
926 "| | | I |\n"
927 "|G|H+---+\n"
928 "| | | J |\n"
929 "+-+-+---+\n"));
932 canvas canvas (table.to_canvas (unicode_theme(), sm));
933 ASSERT_CANVAS_STREQ
934 (canvas, false,
935 // FIXME: are we allowed unicode chars in string literals in our source?
936 ("┌───┬─┬─┐\n"
937 "│ │B│ │\n"
938 "│ ├─┤C│\n"
939 "│ A │D│ │\n"
940 "│ ├─┼─┤\n"
941 "│ │E│F│\n"
942 "├─┬─┼─┴─┤\n"
943 "│ │ │ I │\n"
944 "│G│H├───┤\n"
945 "│ │ │ J │\n"
946 "└─┴─┴───┘\n"));
950 /* Experiment with adding a 1-table-column gap at the boundary between
951 valid vs invalid for visualizing a buffer overflow. */
952 static void
953 test_spans_3 ()
955 const char * const str = "hello world!";
956 const size_t buf_size = 10;
957 const size_t str_size = strlen (str) + 1;
959 style_manager sm;
960 table table (table::size_t (str_size + 1, 3));
962 table.set_cell_span (table::rect_t (table::coord_t (0, 0),
963 table::size_t (str_size + 1, 1)),
964 styled_string (sm, "String literal"));
966 for (size_t i = 0; i < str_size; i++)
968 table::coord_t c (i, 1);
969 if (i >= buf_size)
970 c.x++;
971 if (str[i] == '\0')
972 table.set_cell (c, styled_string (sm, "NUL"));
973 else
974 table.set_cell (c, styled_string ((cppchar_t)str[i]));
977 table.set_cell_span (table::rect_t (table::coord_t (0, 2),
978 table::size_t (buf_size, 1)),
979 styled_string::from_fmt (sm,
980 nullptr,
981 "'buf' (char[%i])",
982 (int)buf_size));
983 table.set_cell_span (table::rect_t (table::coord_t (buf_size + 1, 2),
984 table::size_t (str_size - buf_size, 1)),
985 styled_string (sm, "overflow"));
988 canvas canvas (table.to_canvas (ascii_theme (), sm));
989 ASSERT_CANVAS_STREQ
990 (canvas, false,
991 "+-----------------------------+\n"
992 "| String literal |\n"
993 "+-+-+-+-+-+-+-+-+-+-++-+-+----+\n"
994 "|h|e|l|l|o| |w|o|r|l||d|!|NUL |\n"
995 "+-+-+-+-+-+-+-+-+-+-++-+-+----+\n"
996 "| 'buf' (char[10]) ||overflow|\n"
997 "+-------------------++--------+\n");
1000 canvas canvas (table.to_canvas (unicode_theme (), sm));
1001 ASSERT_CANVAS_STREQ
1002 (canvas, false,
1003 // FIXME: are we allowed unicode chars in string literals in our source?
1004 ("┌─────────────────────────────┐\n"
1005 "│ String literal │\n"
1006 "├─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬┬─┬─┬────┤\n"
1007 "│h│e│l│l│o│ │w│o│r│l││d│!│NUL │\n"
1008 "├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤├─┴─┴────┤\n"
1009 "│ 'buf' (char[10]) ││overflow│\n"
1010 "└───────────────────┘└────────┘\n"));
1014 static void
1015 test_double_width_chars ()
1017 table_cell_content tcc (styled_string ((cppchar_t)0x1f642));
1018 ASSERT_EQ (tcc.get_canvas_size ().w, 2);
1019 ASSERT_EQ (tcc.get_canvas_size ().h, 1);
1021 style_manager sm;
1022 table table (table::size_t (1, 1));
1023 table.set_cell (table::coord_t (0,0),
1024 styled_string ((cppchar_t)0x1f642));
1026 canvas canvas (table.to_canvas (unicode_theme(), sm));
1027 ASSERT_CANVAS_STREQ
1028 (canvas, false,
1029 // FIXME: are we allowed unicode chars in string literals in our source?
1030 ("┌──┐\n"
1031 "│🙂│\n"
1032 "└──┘\n"));
1035 static void
1036 test_ipv4_header ()
1038 style_manager sm;
1039 table table (table::size_t (34, 10));
1040 table.set_cell (table::coord_t (0, 0), styled_string (sm, "Offsets"));
1041 table.set_cell (table::coord_t (1, 0), styled_string (sm, "Octet"));
1042 table.set_cell (table::coord_t (0, 1), styled_string (sm, "Octet"));
1043 for (int octet = 0; octet < 4; octet++)
1044 table.set_cell_span (table::rect_t (table::coord_t (2 + (octet * 8), 0),
1045 table::size_t (8, 1)),
1046 styled_string::from_fmt (sm, nullptr, "%i", octet));
1047 table.set_cell (table::coord_t (1, 1), styled_string (sm, "Bit"));
1048 for (int bit = 0; bit < 32; bit++)
1049 table.set_cell (table::coord_t (bit + 2, 1),
1050 styled_string::from_fmt (sm, nullptr, "%i", bit));
1051 for (int word = 0; word < 6; word++)
1053 table.set_cell (table::coord_t (0, word + 2),
1054 styled_string::from_fmt (sm, nullptr, "%i", word * 4));
1055 table.set_cell (table::coord_t (1, word + 2),
1056 styled_string::from_fmt (sm, nullptr, "%i", word * 32));
1059 table.set_cell (table::coord_t (0, 8), styled_string (sm, "..."));
1060 table.set_cell (table::coord_t (1, 8), styled_string (sm, "..."));
1061 table.set_cell (table::coord_t (0, 9), styled_string (sm, "56"));
1062 table.set_cell (table::coord_t (1, 9), styled_string (sm, "448"));
1064 #define SET_BITS(FIRST, LAST, NAME) \
1065 do { \
1066 const int first = (FIRST); \
1067 const int last = (LAST); \
1068 const char *name = (NAME); \
1069 const int row = first / 32; \
1070 gcc_assert (last / 32 == row); \
1071 table::rect_t rect (table::coord_t ((first % 32) + 2, row + 2), \
1072 table::size_t (last + 1 - first , 1)); \
1073 table.set_cell_span (rect, styled_string (sm, name)); \
1074 } while (0)
1076 SET_BITS (0, 3, "Version");
1077 SET_BITS (4, 7, "IHL");
1078 SET_BITS (8, 13, "DSCP");
1079 SET_BITS (14, 15, "ECN");
1080 SET_BITS (16, 31, "Total Length");
1082 SET_BITS (32 + 0, 32 + 15, "Identification");
1083 SET_BITS (32 + 16, 32 + 18, "Flags");
1084 SET_BITS (32 + 19, 32 + 31, "Fragment Offset");
1086 SET_BITS (64 + 0, 64 + 7, "Time To Live");
1087 SET_BITS (64 + 8, 64 + 15, "Protocol");
1088 SET_BITS (64 + 16, 64 + 31, "Header Checksum");
1090 SET_BITS (96 + 0, 96 + 31, "Source IP Address");
1091 SET_BITS (128 + 0, 128 + 31, "Destination IP Address");
1093 table.set_cell_span(table::rect_t (table::coord_t (2, 7),
1094 table::size_t (32, 3)),
1095 styled_string (sm, "Options"));
1097 canvas canvas (table.to_canvas (ascii_theme(), sm));
1098 ASSERT_CANVAS_STREQ
1099 (canvas, false,
1100 ("+-------+-----+---------------+---------------------+-----------------------+-----------------------+\n"
1101 "|Offsets|Octet| 0 | 1 | 2 | 3 |\n"
1102 "+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\n"
1103 "| Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|\n"
1104 "+-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+\n"
1105 "| 0 | 0 |Version| IHL | DSCP | ECN | Total Length |\n"
1106 "+-------+-----+-------+-------+---------------+-----+--------+--------------------------------------+\n"
1107 "| 4 | 32 | Identification | Flags | Fragment Offset |\n"
1108 "+-------+-----+---------------+---------------------+--------+--------------------------------------+\n"
1109 "| 8 | 64 | Time To Live | Protocol | Header Checksum |\n"
1110 "+-------+-----+---------------+---------------------+-----------------------------------------------+\n"
1111 "| 12 | 96 | Source IP Address |\n"
1112 "+-------+-----+-------------------------------------------------------------------------------------+\n"
1113 "| 16 | 128 | Destination IP Address |\n"
1114 "+-------+-----+-------------------------------------------------------------------------------------+\n"
1115 "| 20 | 160 | |\n"
1116 "+-------+-----+ |\n"
1117 "| ... | ... | Options |\n"
1118 "+-------+-----+ |\n"
1119 "| 56 | 448 | |\n"
1120 "+-------+-----+-------------------------------------------------------------------------------------+\n"));
1123 canvas canvas (table.to_canvas (unicode_theme(), sm));
1124 ASSERT_CANVAS_STREQ
1125 (canvas, false,
1126 // FIXME: are we allowed unicode chars in string literals in our source?
1127 ("┌───────┬─────┬───────────────┬─────────────────────┬───────────────────────┬───────────────────────┐\n"
1128 "│Offsets│Octet│ 0 │ 1 │ 2 │ 3 │\n"
1129 "├───────┼─────┼─┬─┬─┬─┬─┬─┬─┬─┼─┬─┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┼──┬──┬──┬──┬──┬──┬──┬──┤\n"
1130 "│ Octet │ Bit │0│1│2│3│4│5│6│7│8│9│10│11│12│13│14│15│16│17│18│19│20│21│22│23│24│25│26│27│28│29│30│31│\n"
1131 "├───────┼─────┼─┴─┴─┴─┼─┴─┴─┴─┼─┴─┴──┴──┴──┴──┼──┴──┼──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┤\n"
1132 "│ 0 │ 0 │Version│ IHL │ DSCP │ ECN │ Total Length │\n"
1133 "├───────┼─────┼───────┴───────┴───────────────┴─────┼────────┬──────────────────────────────────────┤\n"
1134 "│ 4 │ 32 │ Identification │ Flags │ Fragment Offset │\n"
1135 "├───────┼─────┼───────────────┬─────────────────────┼────────┴──────────────────────────────────────┤\n"
1136 "│ 8 │ 64 │ Time To Live │ Protocol │ Header Checksum │\n"
1137 "├───────┼─────┼───────────────┴─────────────────────┴───────────────────────────────────────────────┤\n"
1138 "│ 12 │ 96 │ Source IP Address │\n"
1139 "├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤\n"
1140 "│ 16 │ 128 │ Destination IP Address │\n"
1141 "├───────┼─────┼─────────────────────────────────────────────────────────────────────────────────────┤\n"
1142 "│ 20 │ 160 │ │\n"
1143 "├───────┼─────┤ │\n"
1144 "│ ... │ ... │ Options │\n"
1145 "├───────┼─────┤ │\n"
1146 "│ 56 │ 448 │ │\n"
1147 "└───────┴─────┴─────────────────────────────────────────────────────────────────────────────────────┘\n"));
1151 static void
1152 test_missing_cells ()
1154 style_manager sm;
1155 table table (table::size_t (3, 3));
1156 table.set_cell (table::coord_t (1, 0), styled_string (sm, "A"));
1157 table.set_cell (table::coord_t (0, 1), styled_string (sm, "B"));
1158 table.set_cell (table::coord_t (1, 1), styled_string (sm, "C"));
1159 table.set_cell (table::coord_t (2, 1), styled_string (sm, "D"));
1160 table.set_cell (table::coord_t (1, 2), styled_string (sm, "E"));
1162 ASSERT_TABLE_NULL_CELL (table, 0, 0);
1163 ASSERT_TABLE_CELL_STREQ (table, 1, 0, "A");
1164 ASSERT_TABLE_NULL_CELL (table, 2, 0);
1166 ASSERT_TABLE_CELL_STREQ (table, 0, 1, "B");
1167 ASSERT_TABLE_CELL_STREQ (table, 1, 1, "C");
1168 ASSERT_TABLE_CELL_STREQ (table, 2, 1, "D");
1170 ASSERT_TABLE_NULL_CELL (table, 0, 2);
1171 ASSERT_TABLE_CELL_STREQ (table, 1, 2, "E");
1172 ASSERT_TABLE_NULL_CELL (table, 2, 2);
1175 canvas canvas (table.to_canvas (ascii_theme(), sm));
1176 ASSERT_CANVAS_STREQ
1177 (canvas, false,
1178 (" +-+\n"
1179 " |A|\n"
1180 "+-+-+-+\n"
1181 "|B|C|D|\n"
1182 "+-+-+-+\n"
1183 " |E|\n"
1184 " +-+\n"));
1187 canvas canvas (table.to_canvas (unicode_theme(), sm));
1188 ASSERT_CANVAS_STREQ
1189 (canvas, false,
1190 (" ┌─┐\n"
1191 " │A│\n"
1192 "┌─┼─┼─┐\n"
1193 "│B│C│D│\n"
1194 "└─┼─┼─┘\n"
1195 " │E│\n"
1196 " └─┘\n"));
1200 static void
1201 test_add_row ()
1203 style_manager sm;
1204 table table (table::size_t (3, 0));
1205 for (int i = 0; i < 5; i++)
1207 const int y = table.add_row ();
1208 for (int x = 0; x < 3; x++)
1209 table.set_cell (table::coord_t (x, y),
1210 styled_string::from_fmt (sm, nullptr,
1211 "%i, %i", x, y));
1213 canvas canvas (table.to_canvas (ascii_theme(), sm));
1214 ASSERT_CANVAS_STREQ
1215 (canvas, false,
1216 ("+----+----+----+\n"
1217 "|0, 0|1, 0|2, 0|\n"
1218 "+----+----+----+\n"
1219 "|0, 1|1, 1|2, 1|\n"
1220 "+----+----+----+\n"
1221 "|0, 2|1, 2|2, 2|\n"
1222 "+----+----+----+\n"
1223 "|0, 3|1, 3|2, 3|\n"
1224 "+----+----+----+\n"
1225 "|0, 4|1, 4|2, 4|\n"
1226 "+----+----+----+\n"));
1229 static void
1230 test_alignment ()
1232 style_manager sm;
1233 table table (table::size_t (9, 9));
1234 table.set_cell_span (table::rect_t (table::coord_t (0, 0),
1235 table::size_t (3, 3)),
1236 styled_string (sm, "left top"),
1237 x_align::LEFT, y_align::TOP);
1238 table.set_cell_span (table::rect_t (table::coord_t (3, 0),
1239 table::size_t (3, 3)),
1240 styled_string (sm, "center top"),
1241 x_align::CENTER, y_align::TOP);
1242 table.set_cell_span (table::rect_t (table::coord_t (6, 0),
1243 table::size_t (3, 3)),
1244 styled_string (sm, "right top"),
1245 x_align::RIGHT, y_align::TOP);
1246 table.set_cell_span (table::rect_t (table::coord_t (0, 3),
1247 table::size_t (3, 3)),
1248 styled_string (sm, "left center"),
1249 x_align::LEFT, y_align::CENTER);
1250 table.set_cell_span (table::rect_t (table::coord_t (3, 3),
1251 table::size_t (3, 3)),
1252 styled_string (sm, "center center"),
1253 x_align::CENTER, y_align::CENTER);
1254 table.set_cell_span (table::rect_t (table::coord_t (6, 3),
1255 table::size_t (3, 3)),
1256 styled_string (sm, "right center"),
1257 x_align::RIGHT, y_align::CENTER);
1258 table.set_cell_span (table::rect_t (table::coord_t (0, 6),
1259 table::size_t (3, 3)),
1260 styled_string (sm, "left bottom"),
1261 x_align::LEFT, y_align::BOTTOM);
1262 table.set_cell_span (table::rect_t (table::coord_t (3, 6),
1263 table::size_t (3, 3)),
1264 styled_string (sm, "center bottom"),
1265 x_align::CENTER, y_align::BOTTOM);
1266 table.set_cell_span (table::rect_t (table::coord_t (6, 6),
1267 table::size_t (3, 3)),
1268 styled_string (sm, "right bottom"),
1269 x_align::RIGHT, y_align::BOTTOM);
1271 canvas canvas (table.to_canvas (ascii_theme(), sm));
1272 ASSERT_CANVAS_STREQ
1273 (canvas, false,
1274 ("+-----------+-------------+------------+\n"
1275 "|left top | center top | right top|\n"
1276 "| | | |\n"
1277 "+-----------+-------------+------------+\n"
1278 "|left center|center center|right center|\n"
1279 "| | | |\n"
1280 "+-----------+-------------+------------+\n"
1281 "| | | |\n"
1282 "|left bottom|center bottom|right bottom|\n"
1283 "+-----------+-------------+------------+\n"));
1286 /* Run all selftests in this file. */
1288 void
1289 text_art_table_cc_tests ()
1291 test_tic_tac_toe ();
1292 test_text_table ();
1293 test_offset_table ();
1294 test_spans ();
1295 test_spans_2 ();
1296 test_spans_3 ();
1297 test_double_width_chars ();
1298 test_ipv4_header ();
1299 test_missing_cells ();
1300 test_add_row ();
1301 test_alignment ();
1304 } // namespace selftest
1307 #endif /* #if CHECKING_P */