Commit: cbf03635c7e1f678da8d1a3becb06b2d7a273929
Parent: 51fe39869616628c7fa70e2386844af8a777dc8c
Author: Randy Palamar
Date: Thu, 23 Jan 2025 07:29:01 -0700
test: add cursor_backwards_tabulation tests from ghostty wiki
Diffstat:
M | tests/test.c | | | 140 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
1 file changed, 115 insertions(+), 25 deletions(-)
diff --git a/tests/test.c b/tests/test.c
@@ -13,16 +13,6 @@ KEYBIND_FN(zoom) { return 0; }
#include "font.c"
#include "terminal.c"
-static void
-get_gpu_glyph_index(Arena a, void *b, void *c, u32 cp, u32 d, u32 e, CachedGlyph **cg)
-{
- /* TODO: this is wrong but will have to do for these tests */
- static CachedGlyph scg;
- if (BETWEEN(cp, ' ', '~')) scg.tile_count = 1;
- else scg.tile_count = 2;
- *cg = &scg;
-}
-
static b32
mem_cmp(void *a_, void *b_, size len)
{
@@ -39,30 +29,45 @@ struct test_result { b32 status; const char *info; };
#define TEST_FN(name) struct test_result name(Term *term, Arena arena)
typedef TEST_FN(Test_Fn);
-#define TESTS \
- X(csi_embedded_control) \
- X(colour_setting) \
- X(cursor_at_line_boundary) \
- X(cursor_movement) \
- X(cursor_tabs) \
- X(cursor_tabs_across_boundary) \
+#define TESTS \
+ X(csi_embedded_control) \
+ X(colour_setting) \
+ X(cursor_at_line_boundary) \
+ X(cursor_movement) \
+ X(cursor_tabs) \
+ X(cursor_tabs_across_boundary) \
X(working_ringbuffer)
+#define GHOSTTY_TESTS \
+ X(cursor_backwards_tabulation_v1) \
+ X(cursor_backwards_tabulation_v2) \
+ X(cursor_backwards_tabulation_v3) \
+ X(cursor_backwards_tabulation_v4)
+
#define X(fn) static TEST_FN(fn);
TESTS
+GHOSTTY_TESTS
#undef X
#define X(fn) fn,
static Test_Fn *tests[] = {
TESTS
+ GHOSTTY_TESTS
};
#undef X
#define ESC(a) s8("\x1B"#a)
#define CSI(a) ESC([a)
-static s8 failure_string = s8("\x1B[31mFAILURE\x1B[0m\n");
-static s8 success_string = s8("\x1B[32mSUCCESS\x1B[0m\n");
+enum {
+ FAILURE = 0,
+ SUCCESS = 1,
+ UNSUPPORTED = 2,
+};
+
+static s8 failure_string = s8("\x1B[31mFAILURE\x1B[0m\n");
+static s8 success_string = s8("\x1B[32mSUCCESS\x1B[0m\n");
+static s8 unsupported_string = s8("\x1B[33mUNSUPPORTED\x1B[0m\n");
static size
copy_into_ringbuf(RingBuf *rb, s8 raw)
@@ -219,7 +224,6 @@ static TEST_FN(cursor_at_line_boundary)
return result;
}
-
static TEST_FN(cursor_movement)
{
struct test_result result = {.info = __FUNCTION__};
@@ -314,6 +318,92 @@ static TEST_FN(working_ringbuffer)
return result;
}
+/***********************************************/
+/* GHOSTTY TESTS (https://ghostty.org/docs/vt) */
+/***********************************************/
+
+/* NOTE: Left Beyond First Column */
+static TEST_FN(cursor_backwards_tabulation_v1)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ launder_static_string(term, s8("\n"));
+ launder_static_string(term, CSI(?5W));
+ launder_static_string(term, CSI(10Z));
+ s8 raw = launder_static_string(term, s8("A"));
+ handle_input(term, arena, raw);
+
+ result.status = term->cursor.pos.x == 1 && term->cursor.pos.y == 1;
+ result.status &= term->views[term->view_idx].fb.rows[1][0].cp == 'A';
+
+ return result;
+}
+
+/* NOTE: Left Starting After Tab Stop */
+static TEST_FN(cursor_backwards_tabulation_v2)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ launder_static_string(term, CSI(?5W));
+ launder_static_string(term, CSI(1;10H));
+ launder_static_string(term, s8("X"));
+ launder_static_string(term, CSI(Z));
+ s8 raw = launder_static_string(term, s8("A"));
+ handle_input(term, arena, raw);
+
+ result.status = term->views[term->view_idx].fb.rows[0][8].cp == 'A';
+ result.status &= term->views[term->view_idx].fb.rows[0][9].cp == 'X';
+
+ return result;
+}
+
+/* NOTE: Left Starting At Tab Stop */
+static TEST_FN(cursor_backwards_tabulation_v3)
+{
+ struct test_result result = {.info = __FUNCTION__};
+
+ launder_static_string(term, CSI(?5W));
+ launder_static_string(term, CSI(1;9H));
+ launder_static_string(term, s8("X"));
+ launder_static_string(term, CSI(1;9H));
+ launder_static_string(term, CSI(Z));
+ s8 raw = launder_static_string(term, s8("A"));
+ handle_input(term, arena, raw);
+
+ result.status = term->views[term->view_idx].fb.rows[0][0].cp == 'A';
+ result.status &= term->views[term->view_idx].fb.rows[0][8].cp == 'X';
+ result.status &= term->views[term->view_idx].fb.rows[0][9].cp == ' ';
+
+ return result;
+}
+
+/* NOTE: Left Margin in Origin Mode */
+static TEST_FN(cursor_backwards_tabulation_v4)
+{
+ struct test_result result = {.info = __FUNCTION__};
+ result.status = UNSUPPORTED;
+
+ #if 0
+ launder_static_string(term, CSI(1;1H));
+ launder_static_string(term, CSI(0J));
+ launder_static_string(term, CSI(?5W));
+ launder_static_string(term, CSI(?6h));
+ launder_static_string(term, CSI(?69h));
+ launder_static_string(term, CSI(3;6s));
+ launder_static_string(term, CSI(1;2H));
+ launder_static_string(term, s8("X"));
+ launder_static_string(term, CSI(Z));
+ s8 raw = launder_static_string(term, s8("A"));
+ handle_input(term, arena, raw);
+
+ result.status = term->views[term->view_idx].fb.rows[0][2].cp == 'A';
+ result.status &= term->views[term->view_idx].fb.rows[0][3].cp == 'X';
+ #endif
+
+ return result;
+}
+
+
static Term *
place_term_into_memory(MemoryBlock memory, i32 rows, i32 columns)
{
@@ -351,6 +441,7 @@ main(void)
u32 max_name_len = 0;
#define X(name) if (sizeof(#name) - 1 > max_name_len) max_name_len = sizeof(#name) - 1;
TESTS
+ GHOSTTY_TESTS
#undef X
max_name_len += 1;
@@ -365,11 +456,10 @@ main(void)
stream_push_s8(&log, s8(":"));
size count = fn.len;
while (count < max_name_len) { stream_push_byte(&log, ' '); count++; }
- if (result.status == 0) {
- failure_count++;
- stream_push_s8(&log, failure_string);
- } else {
- stream_push_s8(&log, success_string);
+ switch (result.status) {
+ case FAILURE: stream_push_s8(&log, failure_string); failure_count++; break;
+ case SUCCESS: stream_push_s8(&log, success_string); break;
+ case UNSUPPORTED: stream_push_s8(&log, unsupported_string); break;
}
os_release_memory_block(term_backing);
}