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