summaryrefslogtreecommitdiff
path: root/tests/helpers/term_api.h
diff options
context:
space:
mode:
Diffstat (limited to 'tests/helpers/term_api.h')
-rw-r--r--tests/helpers/term_api.h182
1 files changed, 182 insertions, 0 deletions
diff --git a/tests/helpers/term_api.h b/tests/helpers/term_api.h
new file mode 100644
index 0000000..bfa8d71
--- /dev/null
+++ b/tests/helpers/term_api.h
@@ -0,0 +1,182 @@
+#ifndef WSAP_TEST_TERM_API_H
+#define WSAP_TEST_TERM_API_H
+
+/*
+ * Short forms of the public terminal API for tests. Cell reads go through
+ * a snapshot, the same way a renderer reads, so tests check only what the
+ * terminal shows.
+ */
+
+#include "term/term.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* The history size the tests assume. */
+#define TEST_SCROLLBACK 4096
+
+static inline struct term *tt_new_opts(int cols, int rows, const struct term_callbacks *cb)
+{
+ struct term_options opt = {
+ .size = { .x = cols, .y = rows },
+ .scrollback = TEST_SCROLLBACK,
+ };
+ if (cb)
+ opt.callbacks = *cb;
+ return term_new(&opt);
+}
+
+static inline struct term *tt_new(int cols, int rows)
+{
+ return tt_new_opts(cols, rows, NULL);
+}
+
+static inline void tt_write(struct term *t, const char *s, size_t len)
+{
+ term_feed(t, s, len, 0);
+}
+
+static inline void tt_puts(struct term *t, const char *s)
+{
+ term_feed(t, s, strlen(s), 0);
+}
+
+static inline int tt_cols(const struct term *t) { return term_size(t).x; }
+static inline int tt_rows(const struct term *t) { return term_size(t).y; }
+static inline int tt_cursor_x(const struct term *t) { return term_cursor(t).pos.x; }
+static inline int tt_cursor_y(const struct term *t) { return term_cursor(t).pos.y; }
+static inline int tt_history(const struct term *t) { return term_scrollback(t).total; }
+static inline int tt_view_offset(const struct term *t) { return term_scrollback(t).offset; }
+
+/* One snapshot serves every read. It follows whichever terminal a test
+ * reads last. */
+static struct term_snapshot *tt_snapshot_for(struct term *t)
+{
+ static struct term_snapshot *snap;
+ if (!snap)
+ snap = term_snapshot_new();
+ term_snapshot_update(snap, t);
+ return snap;
+}
+
+static const struct term_cell tt_blank_cell = {
+ .text = ' ', .fg = TERM_COLOR_FG, .bg = TERM_COLOR_BG, .width = 1,
+};
+
+/* A cell of the viewport. Out-of-range columns read as a blank cell. */
+static inline const struct term_cell *tt_view_cell(struct term *t, int col, int row)
+{
+ struct term_snapshot *s = tt_snapshot_for(t);
+ const struct term_cell *cells = term_snapshot_row(s, row);
+ if (!cells)
+ return NULL;
+ if (col < 0 || col >= term_snapshot_view(s)->size.x)
+ return &tt_blank_cell;
+ return &cells[col];
+}
+
+/* A cell of the live screen, whatever the viewport shows. */
+static inline const struct term_cell *tt_cell(struct term *t, int col, int row)
+{
+ int offset = tt_view_offset(t);
+ if (offset)
+ term_scroll_to(t, 0);
+ const struct term_cell *cell = tt_view_cell(t, col, row);
+ if (offset)
+ term_scroll_to(t, offset);
+ return cell;
+}
+
+/* The code points of a cell's text: one, or a whole cluster. The snapshot
+ * that the last read used must still describe the terminal. */
+static inline int tt_cell_cps(struct term *t, const struct term_cell *cell,
+ uint32_t *cps, int cap)
+{
+ return term_snapshot_cluster(tt_snapshot_for(t), cell->text, cps, cap);
+}
+
+/* The first code point of a cell's text. */
+static inline uint32_t tt_cell_cp(struct term *t, const struct term_cell *cell)
+{
+ uint32_t cp = 0;
+ if (!(cell->text & TERM_TEXT_CLUSTER))
+ return cell->text;
+ tt_cell_cps(t, cell, &cp, 1);
+ return cp;
+}
+
+static inline bool tt_is_cluster(const struct term_cell *cell)
+{
+ return (cell->text & TERM_TEXT_CLUSTER) != 0;
+}
+
+/* The selected columns [begin, end) of a viewport row. */
+static inline struct term_span tt_row_selection(struct term *t, int row)
+{
+ return term_snapshot_row_selection(tt_snapshot_for(t), row);
+}
+
+static inline bool tt_selected(struct term *t, int col, int row)
+{
+ struct term_span span = tt_row_selection(t, row);
+ return col >= span.begin && col < span.end;
+}
+
+/* Select from one viewport cell to another, both inclusive. */
+static inline void tt_select(struct term *t, int x1, int y1, int x2, int y2)
+{
+ bool forward = y1 < y2 || (y1 == y2 && x1 <= x2);
+ struct term_vector2i a = { .x = x1, .y = y1 }, b = { .x = x2, .y = y2 };
+ term_select_start(t, forward ? a : b, false, TERM_SELECT_CELL);
+ term_select_extend(t, forward ? b : a, true);
+}
+
+/* The helpers below return new strings. The caller frees them. */
+/* NULL when nothing is selected. */
+static inline char *tt_selection_text(const struct term *t)
+{
+ size_t n = term_selection_text(t, NULL, 0);
+ if (!n)
+ return NULL;
+ char *text = malloc(n + 1);
+ if (text)
+ term_selection_text(t, text, n + 1);
+ return text;
+}
+
+static inline char *tt_history_text(struct term *t, int max_rows)
+{
+ size_t n = term_history_text(t, max_rows, NULL, 0);
+ char *text = malloc(n + 1);
+ if (text)
+ term_history_text(t, max_rows, text, n + 1);
+ return text;
+}
+
+/* NULL when there is no link. */
+static inline char *tt_link_at(struct term *t, int col, int row)
+{
+ struct term_vector2i cell = { .x = col, .y = row };
+ size_t n = term_link_at(t, cell, NULL, 0);
+ if (!n)
+ return NULL;
+ char *url = malloc(n + 1);
+ if (url)
+ term_link_at(t, cell, url, n + 1);
+ return url;
+}
+
+static inline bool tt_resize(struct term *t, int cols, int rows)
+{
+ return term_resize(t, (struct term_vector2i){ .x = cols, .y = rows });
+}
+
+/* The ARGB value of a color in the terminal's current palette. */
+static inline uint32_t tt_color(struct term *t, term_color c)
+{
+ return term_view_color(term_snapshot_view(tt_snapshot_for(t)), c);
+}
+
+#endif