vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

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:
Mtests/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); }