dte test coverage


Directory: ./
File: test/terminal.c
Date: 2024-12-21 16:03:22
Exec Total Coverage
Lines: 1003 1016 98.7%
Functions: 32 32 100.0%
Branches: 53 60 88.3%

Line Branch Exec Source
1 #include "test.h"
2 #include "terminal/color.h"
3 #include "terminal/cursor.h"
4 #include "terminal/input.h"
5 #include "terminal/key.h"
6 #include "terminal/linux.h"
7 #include "terminal/osc52.h"
8 #include "terminal/output.h"
9 #include "terminal/parse.h"
10 #include "terminal/rxvt.h"
11 #include "terminal/style.h"
12 #include "terminal/terminal.h"
13 #include "util/str-array.h"
14 #include "util/unicode.h"
15 #include "util/utf8.h"
16 #include "util/xsnprintf.h"
17
18 #define TFLAG(flags) (KEYCODE_QUERY_REPLY_BIT | (flags))
19 #define IEXPECT_KEYCODE_EQ(a, b, seq, seq_len) IEXPECT(keycode_eq, a, b, seq, seq_len)
20 #define EXPECT_PARSE_SEQ(seq, expkey) EXPECT(parse_seq, seq, STRLEN(seq), expkey)
21 #define EXPECT_PARSE_SEQN(seq, explen, expkey) EXPECT(parse_seq, seq, explen, expkey)
22
23 1636 static void iexpect_keycode_eq (
24 TestContext *ctx,
25 const char *file,
26 int line,
27 size_t idx,
28 KeyCode a,
29 KeyCode b,
30 const char *seq,
31 size_t seq_len
32 ) {
33
1/2
✓ Branch 0 taken 1636 times.
✗ Branch 1 not taken.
1636 if (likely(a == b)) {
34 1636 test_pass(ctx);
35 1636 return;
36 }
37
38 char a_str[KEYCODE_STR_MAX];
39 char b_str[KEYCODE_STR_MAX];
40 char seq_str[64];
41 keycode_to_string(a, a_str);
42 keycode_to_string(b, b_str);
43 u_make_printable(seq, seq_len, seq_str, sizeof seq_str, 0);
44
45 test_fail(
46 ctx, file, line,
47 "Test #%zu: key codes not equal: 0x%02x, 0x%02x (%s, %s); input: %s",
48 ++idx, a, b, a_str, b_str, seq_str
49 );
50 }
51
52 270 static void expect_parse_seq (
53 TestContext *ctx,
54 const char *file,
55 int line,
56 const char *seq,
57 ssize_t expected_length,
58 KeyCode expected_key
59 ) {
60 270 KeyCode key = 0x18;
61 270 ssize_t parsed_length = term_parse_sequence(seq, strlen(seq), &key);
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 270 times.
270 if (unlikely(parsed_length != expected_length)) {
63 test_fail (
64 ctx, file, line,
65 "term_parse_sequence() returned %zd; expected %zd",
66 parsed_length, expected_length
67 );
68 14 return;
69 }
70
71 270 static_assert(TPARSE_PARTIAL_MATCH == -1);
72
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 256 times.
270 if (expected_length <= 0) {
73 14 test_pass(ctx);
74 14 return;
75 }
76
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 256 times.
256 if (unlikely(key != expected_key)) {
78 char str[2][KEYCODE_STR_MAX];
79 keycode_to_string(key, str[0]);
80 keycode_to_string(expected_key, str[1]);
81 test_fail (
82 ctx, file, line,
83 "Key codes not equal: 0x%02x, 0x%02x (%s, %s)",
84 key, expected_key, str[0], str[1]
85 );
86 return;
87 }
88
89
2/2
✓ Branch 0 taken 1669 times.
✓ Branch 1 taken 256 times.
1925 for (size_t n = expected_length - 1; n != 0; n--) {
90 1669 parsed_length = term_parse_sequence(seq, n, &key);
91
1/2
✓ Branch 0 taken 1669 times.
✗ Branch 1 not taken.
1669 if (parsed_length == TPARSE_PARTIAL_MATCH) {
92 1669 continue;
93 }
94 test_fail (
95 ctx, file, line,
96 "Parsing truncated sequence of %zu bytes returned %zd; expected -1",
97 n, parsed_length
98 );
99 return;
100 }
101
102 256 test_pass(ctx);
103 }
104
105 1 static void test_parse_rgb(TestContext *ctx)
106 {
107 1 EXPECT_EQ(parse_rgb(STRN("f01cff")), COLOR_RGB(0xf01cff));
108 1 EXPECT_EQ(parse_rgb(STRN("011011")), COLOR_RGB(0x011011));
109 1 EXPECT_EQ(parse_rgb(STRN("12aC90")), COLOR_RGB(0x12ac90));
110 1 EXPECT_EQ(parse_rgb(STRN("fffffF")), COLOR_RGB(0xffffff));
111 1 EXPECT_EQ(parse_rgb(STRN("fa0")), COLOR_RGB(0xffaa00));
112 1 EXPECT_EQ(parse_rgb(STRN("123")), COLOR_RGB(0x112233));
113 1 EXPECT_EQ(parse_rgb(STRN("fff")), COLOR_RGB(0xffffff));
114 1 EXPECT_EQ(parse_rgb(STRN("ABC")), COLOR_RGB(0xaabbcc));
115 1 EXPECT_EQ(parse_rgb(STRN("0F0")), COLOR_RGB(0x00ff00));
116 1 EXPECT_EQ(parse_rgb(STRN("000")), COLOR_RGB(0x000000));
117 1 EXPECT_EQ(parse_rgb(STRN("fffffg")), COLOR_INVALID);
118 1 EXPECT_EQ(parse_rgb(STRN(".")), COLOR_INVALID);
119 1 EXPECT_EQ(parse_rgb(STRN("")), COLOR_INVALID);
120 1 EXPECT_EQ(parse_rgb(STRN("11223")), COLOR_INVALID);
121 1 EXPECT_EQ(parse_rgb(STRN("efg")), COLOR_INVALID);
122 1 EXPECT_EQ(parse_rgb(STRN("12")), COLOR_INVALID);
123 1 EXPECT_EQ(parse_rgb(STRN("ffff")), COLOR_INVALID);
124 1 EXPECT_EQ(parse_rgb(STRN("00")), COLOR_INVALID);
125 1 EXPECT_EQ(parse_rgb(STRN("1234567")), COLOR_INVALID);
126 1 EXPECT_EQ(parse_rgb(STRN("123456789")), COLOR_INVALID);
127 1 }
128
129 1 static void test_parse_term_style(TestContext *ctx)
130 {
131 1 static const struct {
132 ssize_t expected_return;
133 const char *const strs[4];
134 TermStyle expected_style;
135 } tests[] = {
136 {3, {"bold", "red", "yellow"}, {COLOR_RED, COLOR_YELLOW, ATTR_BOLD}},
137 {1, {"#ff0000"}, {COLOR_RGB(0xff0000), -1, 0}},
138 {2, {"#f00a9c", "reverse"}, {COLOR_RGB(0xf00a9c), -1, ATTR_REVERSE}},
139 {2, {"black", "#00ffff"}, {COLOR_BLACK, COLOR_RGB(0x00ffff), 0}},
140 {2, {"#123456", "#abcdef"}, {COLOR_RGB(0x123456), COLOR_RGB(0xabcdef), 0}},
141 {2, {"#123", "#fa0"}, {COLOR_RGB(0x112233), COLOR_RGB(0xffaa00), 0}},
142 {2, {"#A1B2C3", "gray"}, {COLOR_RGB(0xa1b2c3), COLOR_GRAY, 0}},
143 {2, {"red", "strikethrough"}, {COLOR_RED, -1, ATTR_STRIKETHROUGH}},
144 {1, {"5/5/5"}, {231, COLOR_DEFAULT, 0}},
145 {3, {"1/3/0", "0/5/2", "italic"}, {70, 48, ATTR_ITALIC}},
146 {2, {"-1", "-2"}, {COLOR_DEFAULT, COLOR_KEEP, 0}},
147 {3, {"keep", "red", "keep"}, {-2, COLOR_RED, ATTR_KEEP}},
148 {2, {"bold", "blink"}, {-1, -1, ATTR_BOLD | ATTR_BLINK}},
149 {3, {"0", "255", "underline"}, {COLOR_BLACK, 255, ATTR_UNDERLINE}},
150 {3, {"white", "green", "dim"}, {COLOR_WHITE, COLOR_GREEN, ATTR_DIM}},
151 {3, {"white", "green", "lowintensity"}, {COLOR_WHITE, COLOR_GREEN, ATTR_DIM}},
152 {2, {"lightred", "lightyellow"}, {COLOR_LIGHTRED, COLOR_LIGHTYELLOW, 0}},
153 {2, {"darkgray", "lightgreen"}, {COLOR_DARKGRAY, COLOR_LIGHTGREEN, 0}},
154 {2, {"lightblue", "lightcyan"}, {COLOR_LIGHTBLUE, COLOR_LIGHTCYAN, 0}},
155 {1, {"lightmagenta"}, {COLOR_LIGHTMAGENTA, COLOR_DEFAULT, 0}},
156 {3, {"keep", "254", "keep"}, {COLOR_KEEP, 254, ATTR_KEEP}},
157 {2, {"keep", "keep"}, {COLOR_KEEP, COLOR_KEEP, 0}},
158 {3, {"red", "green", "keep"}, {COLOR_RED, COLOR_GREEN, ATTR_KEEP}},
159 {3, {"1", "2", "invisible"}, {COLOR_RED, COLOR_GREEN, ATTR_INVIS}},
160 {2, {"default", "0/0/0"}, {COLOR_DEFAULT, 16, 0}},
161 {2, {"2/3/4", "5/5/5"}, {110, 231, 0}},
162 {0, {NULL}, {COLOR_DEFAULT, COLOR_DEFAULT, 0}},
163 // Invalid:
164 {2, {"red", "blue", "invalid"}, {COLOR_INVALID, COLOR_INVALID, 0}},
165 {-1, {"cyan", "magenta", "yellow"}, {COLOR_INVALID, COLOR_INVALID, 0}},
166 {0, {"invalid", "default", "bold"}, {COLOR_INVALID, COLOR_INVALID, 0}},
167 {1, {"italic", "invalid"}, {COLOR_INVALID, COLOR_INVALID, 0}},
168 {2, {"red", "blue", ""}, {COLOR_INVALID, COLOR_INVALID, 0}},
169 {2, {"24", "#abc", "dims"}, {COLOR_INVALID, COLOR_INVALID, 0}},
170 {0, {""}, {COLOR_INVALID, COLOR_INVALID, 0}},
171 {0, {"."}, {COLOR_INVALID, COLOR_INVALID, 0}},
172 {0, {"#"}, {COLOR_INVALID, COLOR_INVALID, 0}},
173 {0, {"-3"}, {COLOR_INVALID, COLOR_INVALID, 0}},
174 {0, {"256"}, {COLOR_INVALID, COLOR_INVALID, 0}},
175 {0, {"brightred"}, {COLOR_INVALID, COLOR_INVALID, 0}},
176 {0, {"lighttblack"}, {COLOR_INVALID, COLOR_INVALID, 0}},
177 {0, {"lightwhite"}, {COLOR_INVALID, COLOR_INVALID, 0}},
178 {0, {"#fffffg"}, {COLOR_INVALID, COLOR_INVALID, 0}},
179 {0, {"#12345"}, {COLOR_INVALID, COLOR_INVALID, 0}},
180 {0, {"123456"}, {COLOR_INVALID, COLOR_INVALID, 0}},
181 {0, {"//0/0"}, {COLOR_INVALID, COLOR_INVALID, 0}},
182 {0, {"0/0/:"}, {COLOR_INVALID, COLOR_INVALID, 0}},
183 {0, {"light_"}, {COLOR_INVALID, COLOR_INVALID, 0}},
184 {0, {"\xFF/0/0"}, {COLOR_INVALID, COLOR_INVALID, 0}},
185 {0, {"1/2/\x9E"}, {COLOR_INVALID, COLOR_INVALID, 0}},
186 };
187
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1 times.
50 FOR_EACH_I(i, tests) {
188 49 const TermStyle expected = tests[i].expected_style;
189 49 TermStyle parsed = {COLOR_INVALID, COLOR_INVALID, 0};
190 49 char **strs = (char**)tests[i].strs;
191 49 ssize_t n = parse_term_style(&parsed, strs, string_array_length(strs));
192 49 IEXPECT_EQ(n, tests[i].expected_return);
193 49 IEXPECT_EQ(parsed.fg, expected.fg);
194 49 IEXPECT_EQ(parsed.bg, expected.bg);
195 49 IEXPECT_EQ(parsed.attr, expected.attr);
196 49 IEXPECT_TRUE(same_style(&parsed, &expected));
197 }
198 1 }
199
200 1 static void test_color_to_nearest(TestContext *ctx)
201 {
202 1 static const struct {
203 int32_t input;
204 int32_t expected_rgb;
205 int32_t expected_256;
206 int32_t expected_16;
207 } tests[] = {
208 // ECMA-48 colors
209 {0, 0, 0, 0},
210 {5, 5, 5, 5},
211 {7, 7, 7, 7},
212
213 // aixterm-style colors
214 {8, 8, 8, 8},
215 {10, 10, 10, 10},
216 {15, 15, 15, 15},
217
218 // xterm 256 palette colors
219 {25, 25, 25, COLOR_BLUE},
220 {87, 87, 87, COLOR_LIGHTCYAN},
221 {88, 88, 88, COLOR_RED},
222 {90, 90, 90, COLOR_MAGENTA},
223 {96, 96, 96, COLOR_MAGENTA},
224 {178, 178, 178, COLOR_YELLOW},
225 {179, 179, 179, COLOR_YELLOW},
226
227 // RGB colors with exact xterm 6x6x6 cube equivalents
228 {COLOR_RGB(0x000000), 16, 16, COLOR_BLACK},
229 {COLOR_RGB(0x000087), 18, 18, COLOR_BLUE},
230 {COLOR_RGB(0x0000FF), 21, 21, COLOR_LIGHTBLUE},
231 {COLOR_RGB(0x00AF87), 36, 36, COLOR_GREEN},
232 {COLOR_RGB(0x00FF00), 46, 46, COLOR_LIGHTGREEN},
233 {COLOR_RGB(0x870000), 88, 88, COLOR_RED},
234 {COLOR_RGB(0xFF0000), 196, 196, COLOR_LIGHTRED},
235 {COLOR_RGB(0xFFD700), 220, 220, COLOR_YELLOW},
236 {COLOR_RGB(0xFFFF5F), 227, 227, COLOR_LIGHTYELLOW},
237 {COLOR_RGB(0xFFFFFF), 231, 231, COLOR_WHITE},
238
239 // RGB colors with exact xterm grayscale equivalents
240 {COLOR_RGB(0x080808), 232, 232, COLOR_BLACK},
241 {COLOR_RGB(0x121212), 233, 233, COLOR_BLACK},
242 {COLOR_RGB(0x6C6C6C), 242, 242, COLOR_DARKGRAY},
243 {COLOR_RGB(0xA8A8A8), 248, 248, COLOR_GRAY},
244 {COLOR_RGB(0xB2B2B2), 249, 249, COLOR_GRAY},
245 {COLOR_RGB(0xBCBCBC), 250, 250, COLOR_WHITE},
246 {COLOR_RGB(0xEEEEEE), 255, 255, COLOR_WHITE},
247
248 // RGB colors with NO exact xterm equivalents
249 {COLOR_RGB(0x00FF88), COLOR_RGB(0x00FF88), 48, COLOR_LIGHTGREEN},
250 {COLOR_RGB(0xFF0001), COLOR_RGB(0xFF0001), 196, COLOR_LIGHTRED},
251 {COLOR_RGB(0xAABBCC), COLOR_RGB(0xAABBCC), 146, COLOR_LIGHTBLUE},
252 {COLOR_RGB(0x010101), COLOR_RGB(0x010101), 16, COLOR_BLACK},
253 {COLOR_RGB(0x070707), COLOR_RGB(0x070707), 232, COLOR_BLACK},
254 {COLOR_RGB(0x080809), COLOR_RGB(0x080809), 232, COLOR_BLACK},
255 {COLOR_RGB(0xBABABA), COLOR_RGB(0xBABABA), 250, COLOR_WHITE},
256 {COLOR_RGB(0xEEEEED), COLOR_RGB(0xEEEEED), 255, COLOR_WHITE},
257 {COLOR_RGB(0xEFEFEF), COLOR_RGB(0xEFEFEF), 255, COLOR_WHITE},
258 {COLOR_RGB(0xF0F0F0), COLOR_RGB(0xF0F0F0), 255, COLOR_WHITE},
259 {COLOR_RGB(0xF6F6F6), COLOR_RGB(0xF6F6F6), 255, COLOR_WHITE},
260 {COLOR_RGB(0xF7F7F7), COLOR_RGB(0xF7F7F7), 231, COLOR_WHITE},
261 {COLOR_RGB(0xFEFEFE), COLOR_RGB(0xFEFEFE), 231, COLOR_WHITE},
262 };
263
264
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 1 times.
44 FOR_EACH_I(i, tests) {
265 43 const int32_t c = tests[i].input;
266 43 IEXPECT_EQ(color_to_nearest(c, TFLAG_TRUE_COLOR, false), c);
267 43 IEXPECT_EQ(color_to_nearest(c, TFLAG_TRUE_COLOR, true), tests[i].expected_rgb);
268 43 IEXPECT_EQ(color_to_nearest(c, TFLAG_256_COLOR, false), tests[i].expected_256);
269 43 IEXPECT_EQ(color_to_nearest(c, TFLAG_16_COLOR, false), tests[i].expected_16);
270 43 IEXPECT_EQ(color_to_nearest(c, TFLAG_8_COLOR, false), tests[i].expected_16 & 7);
271 43 IEXPECT_EQ(color_to_nearest(c, 0, false), COLOR_DEFAULT);
272 }
273
274 static const uint8_t color_stops[6] = {0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
275 size_t count = 0;
276
277
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 for (size_t i = 1; i < ARRAYLEN(color_stops); i++) {
278 5 uint8_t min = color_stops[i - 1];
279 5 uint8_t max = color_stops[i];
280 5 uint8_t mid = min + ((max - min) / 2);
281
2/2
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 5 times.
265 for (unsigned int b = min; b <= max; b++, count++) {
282 260 int32_t orig = COLOR_RGB(b);
283 260 int32_t nearest = color_to_nearest(orig, TFLAG_256_COLOR, false);
284
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 133 times.
260 size_t nearest_stop_idx = (b < mid) ? i - 1 : i;
285 260 EXPECT_TRUE(nearest >= 16);
286 260 EXPECT_TRUE(nearest <= 255);
287
2/2
✓ Branch 0 taken 220 times.
✓ Branch 1 taken 40 times.
260 if (nearest < 232) {
288 220 EXPECT_EQ(nearest, nearest_stop_idx + 16);
289 } else {
290 40 int32_t v = orig & ~COLOR_FLAG_RGB;
291 40 EXPECT_TRUE(v >= 0x0D);
292 40 EXPECT_TRUE(v <= 0x34);
293 40 EXPECT_EQ(nearest, 232 + (((b / 3) - 3) / 10));
294 }
295
2/4
✓ Branch 0 taken 260 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 260 times.
260 if (nearest == min || nearest == max) {
296 EXPECT_EQ(nearest, b);
297 EXPECT_EQ(nearest, color_to_nearest(orig, TFLAG_TRUE_COLOR, true));
298 }
299 445 EXPECT_EQ(nearest_stop_idx, ((uint8_t)b - (b < 75 ? 7 : 35)) / 40);
300 }
301 }
302
303 1 EXPECT_EQ(count, 255 + ARRAYLEN(color_stops) - 1);
304 1 }
305
306 1 static void test_color_to_str(TestContext *ctx)
307 {
308 1 static const struct {
309 char expected_string[15];
310 uint8_t expected_len;
311 int32_t color;
312 } tests[] = {
313 {STRN("keep"), COLOR_KEEP},
314 {STRN("default"), COLOR_DEFAULT},
315 {STRN("black"), COLOR_BLACK},
316 {STRN("gray"), COLOR_GRAY},
317 {STRN("darkgray"), COLOR_DARKGRAY},
318 {STRN("lightmagenta"), COLOR_LIGHTMAGENTA},
319 {STRN("white"), COLOR_WHITE},
320 {STRN("16"), 16},
321 {STRN("255"), 255},
322 {STRN("#abcdef"), COLOR_RGB(0xabcdef)},
323 {STRN("#00001e"), COLOR_RGB(0x00001e)},
324 {STRN("#000000"), COLOR_RGB(0x000000)},
325 {STRN("#100001"), COLOR_RGB(0x100001)},
326 {STRN("#ffffff"), COLOR_RGB(0xffffff)},
327 };
328
329 1 char buf[COLOR_STR_BUFSIZE] = "";
330
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
15 FOR_EACH_I(i, tests) {
331 14 int32_t color = tests[i].color;
332 14 ASSERT_TRUE(color_is_valid(color));
333 14 size_t len = color_to_str(buf, color);
334 14 EXPECT_MEMEQ(buf, len, tests[i].expected_string, tests[i].expected_len);
335 }
336 1 }
337
338 1 static void test_term_style_to_string(TestContext *ctx)
339 {
340 1 static const struct {
341 const char *expected_string;
342 const TermStyle style;
343 } tests[] = {
344 {"red yellow bold", {COLOR_RED, COLOR_YELLOW, ATTR_BOLD}},
345 {"#ff0000", {COLOR_RGB(0xff0000), -1, 0}},
346 {"#f00a9c reverse", {COLOR_RGB(0xf00a9c), -1, ATTR_REVERSE}},
347 {"black #00ffff", {COLOR_BLACK, COLOR_RGB(0x00ffff), 0}},
348 {"#010900", {COLOR_RGB(0x010900), COLOR_DEFAULT, 0}},
349 {"red strikethrough", {COLOR_RED, -1, ATTR_STRIKETHROUGH}},
350 {"231", {231, COLOR_DEFAULT, 0}},
351 {"70 48 italic", {70, 48, ATTR_ITALIC}},
352 {"default keep", {COLOR_DEFAULT, COLOR_KEEP, 0}},
353 {"keep red keep", {COLOR_KEEP, COLOR_RED, ATTR_KEEP}},
354 {"88 16 blink bold", {88, 16, ATTR_BOLD | ATTR_BLINK}},
355 {"black 255 underline", {COLOR_BLACK, 255, ATTR_UNDERLINE}},
356 {"white green dim", {COLOR_WHITE, COLOR_GREEN, ATTR_DIM}},
357 {"darkgray lightgreen", {COLOR_DARKGRAY, COLOR_LIGHTGREEN, 0}},
358 {"lightmagenta", {COLOR_LIGHTMAGENTA, COLOR_DEFAULT, 0}},
359 {"keep 254 keep", {COLOR_KEEP, 254, ATTR_KEEP}},
360 {"default", {COLOR_DEFAULT, COLOR_DEFAULT, 0}},
361 {"default bold", {COLOR_DEFAULT, COLOR_DEFAULT, ATTR_BOLD}},
362 {"default default keep", {COLOR_DEFAULT, COLOR_DEFAULT, ATTR_KEEP}},
363 };
364
365 1 char buf[TERM_STYLE_BUFSIZE];
366
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 times.
20 FOR_EACH_I(i, tests) {
367 19 const char *str = term_style_to_string(buf, &tests[i].style);
368 19 EXPECT_STREQ(str, tests[i].expected_string);
369 }
370
371 // Ensure longest possible color string doesn't overflow the
372 // static buffer in term_style_to_string()
373 1 const TermStyle style = {
374 .fg = COLOR_LIGHTMAGENTA,
375 .bg = COLOR_LIGHTMAGENTA,
376 .attr = ~0U
377 };
378
379 1 static const char expected[] =
380 "lightmagenta lightmagenta "
381 "keep underline reverse blink dim "
382 "bold invisible italic strikethrough"
383 ;
384
385 1 static_assert(sizeof(expected) - 1 == 94);
386 1 const char *str = term_style_to_string(buf, &style);
387 1 EXPECT_STREQ(str, expected);
388 1 }
389
390 1 static void test_cursor_mode_from_str(TestContext *ctx)
391 {
392 1 EXPECT_EQ(cursor_mode_from_str("default"), CURSOR_MODE_DEFAULT);
393 1 EXPECT_EQ(cursor_mode_from_str("insert"), CURSOR_MODE_INSERT);
394 1 EXPECT_EQ(cursor_mode_from_str("overwrite"), CURSOR_MODE_OVERWRITE);
395 1 EXPECT_EQ(cursor_mode_from_str("cmdline"), CURSOR_MODE_CMDLINE);
396 1 EXPECT_EQ(cursor_mode_from_str(""), NR_CURSOR_MODES);
397 1 EXPECT_EQ(cursor_mode_from_str("d"), NR_CURSOR_MODES);
398 1 EXPECT_EQ(cursor_mode_from_str("defaults"), NR_CURSOR_MODES);
399 1 EXPECT_EQ(cursor_mode_from_str("command"), NR_CURSOR_MODES);
400 1 }
401
402 1 static void test_cursor_type_from_str(TestContext *ctx)
403 {
404 1 EXPECT_EQ(cursor_type_from_str("default"), CURSOR_DEFAULT);
405 1 EXPECT_EQ(cursor_type_from_str("keep"), CURSOR_KEEP);
406 1 EXPECT_EQ(cursor_type_from_str("block"), CURSOR_STEADY_BLOCK);
407 1 EXPECT_EQ(cursor_type_from_str("bar"), CURSOR_STEADY_BAR);
408 1 EXPECT_EQ(cursor_type_from_str("underline"), CURSOR_STEADY_UNDERLINE);
409 1 EXPECT_EQ(cursor_type_from_str("blinking-block"), CURSOR_BLINKING_BLOCK);
410 1 EXPECT_EQ(cursor_type_from_str("blinking-bar"), CURSOR_BLINKING_BAR);
411 1 EXPECT_EQ(cursor_type_from_str("blinking-underline"), CURSOR_BLINKING_UNDERLINE);
412 1 EXPECT_EQ(cursor_type_from_str(""), CURSOR_INVALID);
413 1 EXPECT_EQ(cursor_type_from_str("b"), CURSOR_INVALID);
414 1 EXPECT_EQ(cursor_type_from_str("bars"), CURSOR_INVALID);
415 1 EXPECT_EQ(cursor_type_from_str("blinking-default"), CURSOR_INVALID);
416 1 EXPECT_EQ(cursor_type_from_str("blinking-keep"), CURSOR_INVALID);
417 1 }
418
419 1 static void test_cursor_color_from_str(TestContext *ctx)
420 {
421 1 EXPECT_EQ(cursor_color_from_str("keep"), COLOR_KEEP);
422 1 EXPECT_EQ(cursor_color_from_str("default"), COLOR_DEFAULT);
423 1 EXPECT_EQ(cursor_color_from_str("#f9a"), COLOR_RGB(0xFF99AA));
424 1 EXPECT_EQ(cursor_color_from_str("#123456"), COLOR_RGB(0x123456));
425 1 EXPECT_EQ(cursor_color_from_str("red"), COLOR_INVALID);
426 1 EXPECT_EQ(cursor_color_from_str(""), COLOR_INVALID);
427 1 EXPECT_EQ(cursor_color_from_str("#"), COLOR_INVALID);
428 1 EXPECT_EQ(cursor_color_from_str("0"), COLOR_INVALID);
429 1 EXPECT_EQ(cursor_color_from_str("#12345"), COLOR_INVALID);
430 1 EXPECT_EQ(cursor_color_from_str("#1234567"), COLOR_INVALID);
431 1 EXPECT_EQ(cursor_color_from_str("123456"), COLOR_INVALID);
432 1 }
433
434 1 static void test_cursor_color_to_str(TestContext *ctx)
435 {
436 1 char buf[COLOR_STR_BUFSIZE];
437 1 EXPECT_STREQ(cursor_color_to_str(buf, COLOR_DEFAULT), "default");
438 1 EXPECT_STREQ(cursor_color_to_str(buf, COLOR_KEEP), "keep");
439 1 EXPECT_STREQ(cursor_color_to_str(buf, COLOR_RGB(0x190AFE)), "#190afe");
440 1 }
441
442 1 static void test_same_cursor(TestContext *ctx)
443 {
444 1 TermCursorStyle a = get_default_cursor_style(CURSOR_MODE_DEFAULT);
445 1 EXPECT_EQ(a.type, CURSOR_DEFAULT);
446 1 EXPECT_EQ(a.color, COLOR_DEFAULT);
447 1 EXPECT_TRUE(same_cursor(&a, &a));
448
449 1 TermCursorStyle b = get_default_cursor_style(CURSOR_MODE_INSERT);
450 1 EXPECT_EQ(b.type, CURSOR_KEEP);
451 1 EXPECT_EQ(b.color, COLOR_KEEP);
452 1 EXPECT_TRUE(same_cursor(&b, &b));
453
454 1 EXPECT_FALSE(same_cursor(&a, &b));
455 1 b.type = CURSOR_DEFAULT;
456 1 EXPECT_FALSE(same_cursor(&a, &b));
457 1 b.color = COLOR_DEFAULT;
458 1 EXPECT_TRUE(same_cursor(&a, &b));
459 1 }
460
461 1 static void test_term_parse_csi_params(TestContext *ctx)
462 {
463 1 TermControlParams csi = {.nparams = 0};
464 1 StringView s = STRING_VIEW("\033[901;0;55mx");
465 1 size_t n = term_parse_csi_params(s.data, s.length, 2, &csi);
466 1 EXPECT_EQ(n, s.length - 1);
467 1 EXPECT_EQ(csi.nparams, 3);
468 1 EXPECT_EQ(csi.params[0][0], 901);
469 1 EXPECT_EQ(csi.params[1][0], 0);
470 1 EXPECT_EQ(csi.params[2][0], 55);
471 1 EXPECT_EQ(csi.nr_intermediate, 0);
472 1 EXPECT_EQ(csi.final_byte, 'm');
473 1 EXPECT_FALSE(csi.have_subparams);
474 1 EXPECT_FALSE(csi.unhandled_bytes);
475
476 1 csi = (TermControlParams){.nparams = 0};
477 1 s = strview_from_cstring("\033[123;09;56:78:99m");
478 1 n = term_parse_csi_params(s.data, s.length, 2, &csi);
479 1 EXPECT_EQ(n, s.length);
480 1 EXPECT_EQ(csi.nparams, 3);
481 1 static_assert(ARRAYLEN(csi.nsub) >= 4);
482 1 static_assert(ARRAYLEN(csi.nsub) == ARRAYLEN(csi.params));
483 1 EXPECT_EQ(csi.nsub[0], 1);
484 1 EXPECT_EQ(csi.nsub[1], 1);
485 1 EXPECT_EQ(csi.nsub[2], 3);
486 1 EXPECT_EQ(csi.nsub[3], 0);
487 1 EXPECT_EQ(csi.params[0][0], 123);
488 1 EXPECT_EQ(csi.params[1][0], 9);
489 1 EXPECT_EQ(csi.params[2][0], 56);
490 1 EXPECT_EQ(csi.params[2][1], 78);
491 1 EXPECT_EQ(csi.params[2][2], 99);
492 1 EXPECT_EQ(csi.params[3][0], 0);
493 1 EXPECT_EQ(csi.nr_intermediate, 0);
494 1 EXPECT_EQ(csi.final_byte, 'm');
495 1 EXPECT_TRUE(csi.have_subparams);
496 1 EXPECT_FALSE(csi.unhandled_bytes);
497
498 1 csi = (TermControlParams){.nparams = 0};
499 1 s = strview_from_cstring("\033[1:2:3;44:55;;6~");
500 1 n = term_parse_csi_params(s.data, s.length, 2, &csi);
501 1 EXPECT_EQ(n, s.length);
502 1 EXPECT_EQ(csi.nparams, 4);
503 1 EXPECT_EQ(csi.nsub[0], 3);
504 1 EXPECT_EQ(csi.nsub[1], 2);
505 1 EXPECT_EQ(csi.nsub[2], 1);
506 1 EXPECT_EQ(csi.params[0][0], 1);
507 1 EXPECT_EQ(csi.params[0][1], 2);
508 1 EXPECT_EQ(csi.params[0][2], 3);
509 1 EXPECT_EQ(csi.params[1][0], 44);
510 1 EXPECT_EQ(csi.params[1][1], 55);
511 1 EXPECT_EQ(csi.params[2][0], 0);
512 1 EXPECT_EQ(csi.params[3][0], 6);
513 1 EXPECT_EQ(csi.nr_intermediate, 0);
514 1 EXPECT_EQ(csi.final_byte, '~');
515 1 EXPECT_TRUE(csi.have_subparams);
516 1 EXPECT_FALSE(csi.unhandled_bytes);
517
518 1 csi = (TermControlParams){.nparams = 0};
519 1 s = strview_from_cstring("\033[+2p");
520 1 n = term_parse_csi_params(s.data, s.length, 2, &csi);
521 1 EXPECT_EQ(n, s.length);
522 1 EXPECT_EQ(csi.nparams, 1);
523 1 EXPECT_EQ(csi.nsub[0], 1);
524 1 EXPECT_EQ(csi.params[0][0], 2);
525 1 EXPECT_EQ(csi.nr_intermediate, 1);
526 1 EXPECT_EQ(csi.intermediate[0], '+');
527 1 EXPECT_EQ(csi.final_byte, 'p');
528 1 EXPECT_FALSE(csi.have_subparams);
529 1 EXPECT_FALSE(csi.unhandled_bytes);
530
531 1 csi = (TermControlParams){.nparams = 0};
532 1 s = strview_from_cstring("\033[?47;1$y");
533 1 n = term_parse_csi_params(s.data, s.length, 2, &csi);
534 1 EXPECT_EQ(n, s.length);
535 1 EXPECT_EQ(csi.nparams, 2);
536 1 EXPECT_EQ(csi.params[0][0], 47);
537 1 EXPECT_EQ(csi.params[1][0], 1);
538 1 EXPECT_EQ(csi.nr_intermediate, 1);
539 1 EXPECT_EQ(csi.intermediate[0], '$');
540 1 EXPECT_FALSE(csi.have_subparams);
541 // Note: question mark param prefixes are handled in parse_csi() instead
542 // of term_parse_csi_params(), so the latter reports it as an unhandled byte
543 1 EXPECT_TRUE(csi.unhandled_bytes);
544 1 }
545
546 1 static void test_term_parse_sequence(TestContext *ctx)
547 {
548 1 EXPECT_PARSE_SEQ("", 0);
549 1 EXPECT_PARSE_SEQN("x", 0, 0);
550 1 EXPECT_PARSE_SEQN("\033q", 0, 0);
551 1 EXPECT_PARSE_SEQ("\033\\", KEY_IGNORE);
552
553 1 EXPECT_PARSE_SEQ("\033[Z", MOD_SHIFT | KEY_TAB);
554 1 EXPECT_PARSE_SEQ("\033[1;2A", MOD_SHIFT | KEY_UP);
555 1 EXPECT_PARSE_SEQ("\033[1;2B", MOD_SHIFT | KEY_DOWN);
556 1 EXPECT_PARSE_SEQ("\033[1;2C", MOD_SHIFT | KEY_RIGHT);
557 1 EXPECT_PARSE_SEQ("\033[1;2D", MOD_SHIFT | KEY_LEFT);
558 1 EXPECT_PARSE_SEQ("\033[1;2E", MOD_SHIFT | KEY_BEGIN);
559 1 EXPECT_PARSE_SEQ("\033[1;2F", MOD_SHIFT | KEY_END);
560 1 EXPECT_PARSE_SEQ("\033[1;2H", MOD_SHIFT | KEY_HOME);
561 1 EXPECT_PARSE_SEQ("\033[1;8H", MOD_SHIFT | MOD_META | MOD_CTRL | KEY_HOME);
562 1 EXPECT_PARSE_SEQN("\033[1;8H~", 6, MOD_SHIFT | MOD_META | MOD_CTRL | KEY_HOME);
563 1 EXPECT_PARSE_SEQN("\033[1;8H~_", 6, MOD_SHIFT | MOD_META | MOD_CTRL | KEY_HOME);
564 1 EXPECT_PARSE_SEQN("\033", TPARSE_PARTIAL_MATCH, 0);
565 1 EXPECT_PARSE_SEQN("\033[", TPARSE_PARTIAL_MATCH, 0);
566 1 EXPECT_PARSE_SEQN("\033]", TPARSE_PARTIAL_MATCH, 0);
567 1 EXPECT_PARSE_SEQN("\033[1", TPARSE_PARTIAL_MATCH, 0);
568 1 EXPECT_PARSE_SEQN("\033[9", TPARSE_PARTIAL_MATCH, 0);
569 1 EXPECT_PARSE_SEQN("\033[1;", TPARSE_PARTIAL_MATCH, 0);
570 1 EXPECT_PARSE_SEQN("\033[1[", 4, KEY_IGNORE);
571 1 EXPECT_PARSE_SEQN("\033[1;2", TPARSE_PARTIAL_MATCH, 0);
572 1 EXPECT_PARSE_SEQN("\033[1;8", TPARSE_PARTIAL_MATCH, 0);
573 1 EXPECT_PARSE_SEQN("\033[1;9", TPARSE_PARTIAL_MATCH, 0);
574 1 EXPECT_PARSE_SEQ("\033[1;_", KEY_IGNORE);
575 1 EXPECT_PARSE_SEQ("\033[1;8Z", KEY_IGNORE);
576 1 EXPECT_PARSE_SEQN("\033O", TPARSE_PARTIAL_MATCH, 0);
577 1 EXPECT_PARSE_SEQN("\033[\033", 2, KEY_IGNORE);
578 1 EXPECT_PARSE_SEQN("\033[\030", 3, KEY_IGNORE);
579 1 EXPECT_PARSE_SEQ("\033[A", KEY_UP);
580 1 EXPECT_PARSE_SEQ("\033[B", KEY_DOWN);
581 1 EXPECT_PARSE_SEQ("\033[C", KEY_RIGHT);
582 1 EXPECT_PARSE_SEQ("\033[D", KEY_LEFT);
583 1 EXPECT_PARSE_SEQ("\033[E", KEY_BEGIN);
584 1 EXPECT_PARSE_SEQ("\033[F", KEY_END);
585 1 EXPECT_PARSE_SEQ("\033[H", KEY_HOME);
586 1 EXPECT_PARSE_SEQ("\033[L", KEY_INSERT);
587 1 EXPECT_PARSE_SEQ("\033[P", KEY_F1);
588 1 EXPECT_PARSE_SEQ("\033[Q", KEY_F2);
589 1 EXPECT_PARSE_SEQ("\033[R", KEY_F3);
590 1 EXPECT_PARSE_SEQ("\033[S", KEY_F4);
591 1 EXPECT_PARSE_SEQ("\033O ", KEY_SPACE);
592 1 EXPECT_PARSE_SEQ("\033OA", KEY_UP);
593 1 EXPECT_PARSE_SEQ("\033OB", KEY_DOWN);
594 1 EXPECT_PARSE_SEQ("\033OC", KEY_RIGHT);
595 1 EXPECT_PARSE_SEQ("\033OD", KEY_LEFT);
596 1 EXPECT_PARSE_SEQ("\033OE", KEY_BEGIN);
597 1 EXPECT_PARSE_SEQ("\033OF", KEY_END);
598 1 EXPECT_PARSE_SEQ("\033OH", KEY_HOME);
599 1 EXPECT_PARSE_SEQ("\033OI", KEY_TAB);
600 1 EXPECT_PARSE_SEQ("\033OJ", KEY_IGNORE);
601 1 EXPECT_PARSE_SEQ("\033OM", KEY_ENTER);
602 1 EXPECT_PARSE_SEQ("\033OP", KEY_F1);
603 1 EXPECT_PARSE_SEQ("\033OQ", KEY_F2);
604 1 EXPECT_PARSE_SEQ("\033OR", KEY_F3);
605 1 EXPECT_PARSE_SEQ("\033OS", KEY_F4);
606 1 EXPECT_PARSE_SEQ("\033OT", KEY_IGNORE);
607 1 EXPECT_PARSE_SEQ("\033OX", '=');
608 1 EXPECT_PARSE_SEQ("\033Oi", KEY_IGNORE);
609 1 EXPECT_PARSE_SEQ("\033Oj", '*');
610 1 EXPECT_PARSE_SEQ("\033Ok", '+');
611 1 EXPECT_PARSE_SEQ("\033Ol", ',');
612 1 EXPECT_PARSE_SEQ("\033Om", '-');
613 1 EXPECT_PARSE_SEQ("\033On", '.');
614 1 EXPECT_PARSE_SEQ("\033Oo", '/');
615 1 EXPECT_PARSE_SEQ("\033Op", '0');
616 1 EXPECT_PARSE_SEQ("\033Oq", '1');
617 1 EXPECT_PARSE_SEQ("\033Or", '2');
618 1 EXPECT_PARSE_SEQ("\033Os", '3');
619 1 EXPECT_PARSE_SEQ("\033Ot", '4');
620 1 EXPECT_PARSE_SEQ("\033Ou", '5');
621 1 EXPECT_PARSE_SEQ("\033Ov", '6');
622 1 EXPECT_PARSE_SEQ("\033Ow", '7');
623 1 EXPECT_PARSE_SEQ("\033Ox", '8');
624 1 EXPECT_PARSE_SEQ("\033Oy", '9');
625 1 EXPECT_PARSE_SEQ("\033Oz", KEY_IGNORE);
626 1 EXPECT_PARSE_SEQ("\033O~", KEY_IGNORE);
627 1 EXPECT_PARSE_SEQ("\033[1~", KEY_HOME);
628 1 EXPECT_PARSE_SEQ("\033[2~", KEY_INSERT);
629 1 EXPECT_PARSE_SEQ("\033[3~", KEY_DELETE);
630 1 EXPECT_PARSE_SEQ("\033[4~", KEY_END);
631 1 EXPECT_PARSE_SEQ("\033[5~", KEY_PAGE_UP);
632 1 EXPECT_PARSE_SEQ("\033[6~", KEY_PAGE_DOWN);
633 1 EXPECT_PARSE_SEQ("\033[7~", KEY_HOME);
634 1 EXPECT_PARSE_SEQ("\033[8~", KEY_END);
635 1 EXPECT_PARSE_SEQ("\033[10~", KEY_IGNORE);
636 1 EXPECT_PARSE_SEQ("\033[11~", KEY_F1);
637 1 EXPECT_PARSE_SEQ("\033[12~", KEY_F2);
638 1 EXPECT_PARSE_SEQ("\033[13~", KEY_F3);
639 1 EXPECT_PARSE_SEQ("\033[14~", KEY_F4);
640 1 EXPECT_PARSE_SEQ("\033[15~", KEY_F5);
641 1 EXPECT_PARSE_SEQ("\033[16~", KEY_IGNORE);
642 1 EXPECT_PARSE_SEQ("\033[17~", KEY_F6);
643 1 EXPECT_PARSE_SEQ("\033[18~", KEY_F7);
644 1 EXPECT_PARSE_SEQ("\033[19~", KEY_F8);
645 1 EXPECT_PARSE_SEQ("\033[20~", KEY_F9);
646 1 EXPECT_PARSE_SEQ("\033[21~", KEY_F10);
647 1 EXPECT_PARSE_SEQ("\033[22~", KEY_IGNORE);
648 1 EXPECT_PARSE_SEQ("\033[23~", KEY_F11);
649 1 EXPECT_PARSE_SEQ("\033[24~", KEY_F12);
650 1 EXPECT_PARSE_SEQ("\033[25~", KEY_F13);
651 1 EXPECT_PARSE_SEQ("\033[26~", KEY_F14);
652 1 EXPECT_PARSE_SEQ("\033[27~", KEY_IGNORE);
653 1 EXPECT_PARSE_SEQ("\033[28~", KEY_F15);
654 1 EXPECT_PARSE_SEQ("\033[29~", KEY_F16);
655 1 EXPECT_PARSE_SEQ("\033[30~", KEY_IGNORE);
656 1 EXPECT_PARSE_SEQ("\033[31~", KEY_F17);
657 1 EXPECT_PARSE_SEQ("\033[34~", KEY_F20);
658 1 EXPECT_PARSE_SEQ("\033[35~", KEY_IGNORE);
659 1 EXPECT_PARSE_SEQ("\033[6;3~", MOD_META | KEY_PAGE_DOWN);
660 1 EXPECT_PARSE_SEQ("\033[6;5~", MOD_CTRL | KEY_PAGE_DOWN);
661 1 EXPECT_PARSE_SEQ("\033[6;8~", MOD_SHIFT | MOD_META | MOD_CTRL | KEY_PAGE_DOWN);
662
663 // xterm + `modifyOtherKeys` option
664 1 EXPECT_PARSE_SEQ("\033[27;5;9~", MOD_CTRL | KEY_TAB);
665 1 EXPECT_PARSE_SEQ("\033[27;5;27~", MOD_CTRL | KEY_ESCAPE);
666 1 EXPECT_PARSE_SEQ("\033[27;8;27~", MOD_CTRL | MOD_META | MOD_SHIFT | KEY_ESCAPE);
667 1 EXPECT_PARSE_SEQ("\033[27;1;27~", KEY_ESCAPE); // Tested in principle only; xterm never sends this
668 1 EXPECT_PARSE_SEQ("\033[27;5;13~", MOD_CTRL | KEY_ENTER);
669 1 EXPECT_PARSE_SEQ("\033[27;6;13~", MOD_CTRL | MOD_SHIFT | KEY_ENTER);
670 1 EXPECT_PARSE_SEQ("\033[27;2;127~", MOD_SHIFT | KEY_BACKSPACE);
671 1 EXPECT_PARSE_SEQ("\033[27;6;127~", MOD_CTRL | MOD_SHIFT | KEY_BACKSPACE);
672 1 EXPECT_PARSE_SEQ("\033[27;8;127~", MOD_CTRL | MOD_META | MOD_SHIFT | KEY_BACKSPACE);
673 1 EXPECT_PARSE_SEQ("\033[27;2;8~", MOD_SHIFT | KEY_BACKSPACE);
674 1 EXPECT_PARSE_SEQ("\033[27;6;8~", MOD_CTRL | MOD_SHIFT | KEY_BACKSPACE);
675 1 EXPECT_PARSE_SEQ("\033[27;8;8~", MOD_CTRL | MOD_META | MOD_SHIFT | KEY_BACKSPACE);
676 1 EXPECT_PARSE_SEQ("\033[27;6;82~", MOD_CTRL | MOD_SHIFT | 'r');
677 1 EXPECT_PARSE_SEQ("\033[27;5;114~", MOD_CTRL | 'r');
678 1 EXPECT_PARSE_SEQ("\033[27;3;82~", MOD_META | MOD_SHIFT | 'r'); // Sent by tmux 3.5-76-gbbc3cc55 (xterm sends "\033[27;4;82~")
679 1 EXPECT_PARSE_SEQ("\033[27;3;114~", MOD_META | 'r');
680 // EXPECT_PARSE_SEQ("\033[27;4;62~", MOD_META | '>');
681 1 EXPECT_PARSE_SEQ("\033[27;5;46~", MOD_CTRL | '.');
682 1 EXPECT_PARSE_SEQ("\033[27;3;1114111~", MOD_META | UNICODE_MAX_VALID_CODEPOINT);
683 1 EXPECT_PARSE_SEQ("\033[27;3;1114112~", KEY_IGNORE);
684 1 EXPECT_PARSE_SEQ("\033[27;999999999999999999999;123~", KEY_IGNORE);
685 1 EXPECT_PARSE_SEQ("\033[27;123;99999999999999999~", KEY_IGNORE);
686
687 // www.leonerd.org.uk/hacks/fixterms/
688 1 EXPECT_PARSE_SEQ("\033[13;3u", MOD_META | KEY_ENTER);
689 1 EXPECT_PARSE_SEQ("\033[9;5u", MOD_CTRL | KEY_TAB);
690 1 EXPECT_PARSE_SEQ("\033[65;3u", MOD_META | 'A');
691 1 EXPECT_PARSE_SEQ("\033[108;5u", MOD_CTRL | 'l');
692 1 EXPECT_PARSE_SEQ("\033[127765;3u", MOD_META | 127765ul);
693 1 EXPECT_PARSE_SEQ("\033[1114111;3u", MOD_META | UNICODE_MAX_VALID_CODEPOINT);
694 1 EXPECT_PARSE_SEQ("\033[1114112;3u", KEY_IGNORE);
695 1 EXPECT_PARSE_SEQ("\033[11141110;3u", KEY_IGNORE);
696 1 EXPECT_PARSE_SEQ("\033[11141111;3u", KEY_IGNORE);
697 1 EXPECT_PARSE_SEQ("\033[2147483647;3u", KEY_IGNORE); // INT32_MAX
698 1 EXPECT_PARSE_SEQ("\033[2147483648;3u", KEY_IGNORE); // INT32_MAX + 1
699 1 EXPECT_PARSE_SEQ("\033[4294967295;3u", KEY_IGNORE); // UINT32_MAX
700 1 EXPECT_PARSE_SEQ("\033[4294967296;3u", KEY_IGNORE); // UINT32_MAX + 1
701 1 EXPECT_PARSE_SEQ("\033[-1;3u", KEY_IGNORE);
702 1 EXPECT_PARSE_SEQ("\033[-2;3u", KEY_IGNORE);
703 1 EXPECT_PARSE_SEQ("\033[ 2;3u", KEY_IGNORE);
704 1 EXPECT_PARSE_SEQ("\033[<?>2;3u", KEY_IGNORE);
705 1 EXPECT_PARSE_SEQ("\033[ !//.$2;3u", KEY_IGNORE);
706
707 // https://sw.kovidgoyal.net/kitty/keyboard-protocol
708 1 EXPECT_PARSE_SEQ("\033[27u", KEY_ESCAPE);
709 1 EXPECT_PARSE_SEQ("\033[27;5u", MOD_CTRL | KEY_ESCAPE);
710 1 EXPECT_PARSE_SEQ("\033[27;8u", MOD_CTRL | MOD_META | MOD_SHIFT | KEY_ESCAPE);
711 1 EXPECT_PARSE_SEQ("\033[57359u", KEY_SCROLL_LOCK);
712 1 EXPECT_PARSE_SEQ("\033[57360u", KEY_IGNORE);
713 1 EXPECT_PARSE_SEQ("\033[57361u", KEY_PRINT_SCREEN);
714 1 EXPECT_PARSE_SEQ("\033[57362u", KEY_PAUSE);
715 1 EXPECT_PARSE_SEQ("\033[57363u", KEY_MENU);
716 1 EXPECT_PARSE_SEQ("\033[57376u", KEY_F13);
717 1 EXPECT_PARSE_SEQ("\033[57377u", KEY_F14);
718 1 EXPECT_PARSE_SEQ("\033[57378u", KEY_F15);
719 1 EXPECT_PARSE_SEQ("\033[57379u", KEY_F16);
720 1 EXPECT_PARSE_SEQ("\033[57380u", KEY_F17);
721 1 EXPECT_PARSE_SEQ("\033[57381u", KEY_F18);
722 1 EXPECT_PARSE_SEQ("\033[57382u", KEY_F19);
723 1 EXPECT_PARSE_SEQ("\033[57383u", KEY_F20);
724 1 EXPECT_PARSE_SEQ("\033[57399u", '0');
725 1 EXPECT_PARSE_SEQ("\033[57400u", '1');
726 1 EXPECT_PARSE_SEQ("\033[57401u", '2');
727 1 EXPECT_PARSE_SEQ("\033[57402u", '3');
728 1 EXPECT_PARSE_SEQ("\033[57403u", '4');
729 1 EXPECT_PARSE_SEQ("\033[57404u", '5');
730 1 EXPECT_PARSE_SEQ("\033[57405u", '6');
731 1 EXPECT_PARSE_SEQ("\033[57406u", '7');
732 1 EXPECT_PARSE_SEQ("\033[57407u", '8');
733 1 EXPECT_PARSE_SEQ("\033[57408u", '9');
734 1 EXPECT_PARSE_SEQ("\033[57409u", '.');
735 1 EXPECT_PARSE_SEQ("\033[57410u", '/');
736 1 EXPECT_PARSE_SEQ("\033[57411u", '*');
737 1 EXPECT_PARSE_SEQ("\033[57412u", '-');
738 1 EXPECT_PARSE_SEQ("\033[57413u", '+');
739 1 EXPECT_PARSE_SEQ("\033[57414u", KEY_ENTER);
740 1 EXPECT_PARSE_SEQ("\033[57415u", '=');
741 1 EXPECT_PARSE_SEQ("\033[57417u", KEY_LEFT);
742 1 EXPECT_PARSE_SEQ("\033[57418u", KEY_RIGHT);
743 1 EXPECT_PARSE_SEQ("\033[57419u", KEY_UP);
744 1 EXPECT_PARSE_SEQ("\033[57420u", KEY_DOWN);
745 1 EXPECT_PARSE_SEQ("\033[57421u", KEY_PAGE_UP);
746 1 EXPECT_PARSE_SEQ("\033[57422u", KEY_PAGE_DOWN);
747 1 EXPECT_PARSE_SEQ("\033[57423u", KEY_HOME);
748 1 EXPECT_PARSE_SEQ("\033[57424u", KEY_END);
749 1 EXPECT_PARSE_SEQ("\033[57425u", KEY_INSERT);
750 1 EXPECT_PARSE_SEQ("\033[57426u", KEY_DELETE);
751 1 EXPECT_PARSE_SEQ("\033[57427u", KEY_BEGIN);
752 1 EXPECT_PARSE_SEQ("\033[57361;2u", MOD_SHIFT | KEY_PRINT_SCREEN);
753 1 EXPECT_PARSE_SEQ("\033[3615:3620:97;6u", MOD_CTRL | MOD_SHIFT | 'a');
754 1 EXPECT_PARSE_SEQ("\033[97;5u", MOD_CTRL | 'a');
755 1 EXPECT_PARSE_SEQ("\033[97;5:1u", MOD_CTRL | 'a');
756 1 EXPECT_PARSE_SEQ("\033[97;5:2u", MOD_CTRL | 'a');
757 1 EXPECT_PARSE_SEQ("\033[97;5:3u", KEY_IGNORE);
758 // TODO: EXPECT_PARSE_SEQ("\033[97;5:u", MOD_CTRL | 'a');
759 1 EXPECT_PARSE_SEQ("\033[104;5u", MOD_CTRL | 'h');
760 1 EXPECT_PARSE_SEQ("\033[104;9u", MOD_SUPER | 'h');
761 1 EXPECT_PARSE_SEQ("\033[104;17u", MOD_HYPER | 'h');
762 1 EXPECT_PARSE_SEQ("\033[104;10u", MOD_SUPER | MOD_SHIFT | 'h');
763 1 EXPECT_PARSE_SEQ("\033[116;69u", MOD_CTRL | 't'); // Ignored Capslock in modifiers
764 1 EXPECT_PARSE_SEQ("\033[116;133u", MOD_CTRL | 't'); // Ignored Numlock in modifiers
765 1 EXPECT_PARSE_SEQ("\033[116;256u", MOD_MASK | 't'); // Ignored Capslock and Numlock
766 1 EXPECT_PARSE_SEQ("\033[116;257u", KEY_IGNORE); // Unknown bit in modifiers
767
768 // Excess params
769 1 EXPECT_PARSE_SEQ("\033[1;2;3;4;5;6;7;8;9m", KEY_IGNORE);
770
771 // DA1 replies
772 1 EXPECT_PARSE_SEQ("\033[?64;15;22c", TFLAG(TFLAG_QUERY_L2 | TFLAG_QUERY_L3 | TFLAG_8_COLOR));
773 1 EXPECT_PARSE_SEQ("\033[?1;2c", TFLAG(TFLAG_QUERY_L2));
774 1 EXPECT_PARSE_SEQ("\033[?90c", KEY_IGNORE);
775
776 // DA2 replies
777 1 EXPECT_PARSE_SEQ("\033[>0;136;0c", KEY_IGNORE);
778 1 EXPECT_PARSE_SEQ("\033[>84;0;0c", TFLAG(TFLAG_QUERY_L3)); // tmux
779 1 EXPECT_PARSE_SEQ("\033[>1;11801;0c", TFLAG(TFLAG_QUERY_L3)); // foot
780
781 // DA3 replies
782 1 EXPECT_PARSE_SEQN("\033P!|464f4f54\033\\", 12, TFLAG(TFLAG_QUERY_L3)); // foot
783 1 EXPECT_PARSE_SEQN("\033P!|464f4f54\033", 12, TFLAG(TFLAG_QUERY_L3));
784 1 EXPECT_PARSE_SEQN("\033P!|464f4f54", TPARSE_PARTIAL_MATCH, 0);
785
786 // XTVERSION replies
787 1 const TermFeatureFlags tmuxflags = TFLAG (
788 TFLAG_NO_QUERY_L3 | TFLAG_ECMA48_REPEAT | TFLAG_MODIFY_OTHER_KEYS
789 );
790 1 EXPECT_PARSE_SEQN("\033P>|tmux 3.2\033\\", 12, tmuxflags);
791 1 EXPECT_PARSE_SEQN("\033P>|tmux 3.2a\033\\", 13, tmuxflags);
792 1 EXPECT_PARSE_SEQN("\033P>|tmux 3.2-rc2\033\\", 16, tmuxflags);
793 1 EXPECT_PARSE_SEQN("\033P>|tmux next-3.3\033\\", 17, tmuxflags);
794 1 EXPECT_PARSE_SEQN("\033P>|xyz\033\\", 7, TFLAG(TFLAG_QUERY_L3));
795
796 // XTMODKEYS (modifyOtherKeys mode query) replies
797 1 EXPECT_PARSE_SEQ("\033[>4;0m", TFLAG(TFLAG_MODIFY_OTHER_KEYS));
798 1 EXPECT_PARSE_SEQ("\033[>4;1m", TFLAG(TFLAG_MODIFY_OTHER_KEYS));
799 1 EXPECT_PARSE_SEQ("\033[>4;2m", TFLAG(TFLAG_MODIFY_OTHER_KEYS));
800 1 EXPECT_PARSE_SEQ("\033[>4;3m", KEY_IGNORE);
801 1 EXPECT_PARSE_SEQ("\033[>4;m", TFLAG(TFLAG_MODIFY_OTHER_KEYS));
802 1 EXPECT_PARSE_SEQ("\033[>4m", TFLAG(TFLAG_MODIFY_OTHER_KEYS));
803 1 EXPECT_PARSE_SEQ("\033[>4;2;0m", KEY_IGNORE);
804
805 // XTWINOPS replies
806 1 EXPECT_PARSE_SEQN("\033]ltitle\033\\", 8, KEY_IGNORE);
807 1 EXPECT_PARSE_SEQN("\033]Licon\033\\", 7, KEY_IGNORE);
808 1 EXPECT_PARSE_SEQ("\033]ltitle\a", KEY_IGNORE);
809 1 EXPECT_PARSE_SEQ("\033]Licon\a", KEY_IGNORE);
810
811 // DECRPM replies (to DECRQM queries)
812 1 EXPECT_PARSE_SEQ("\033[?2026;0$y", KEY_IGNORE);
813 1 EXPECT_PARSE_SEQ("\033[?2026;1$y", TFLAG(TFLAG_SYNC));
814 1 EXPECT_PARSE_SEQ("\033[?2026;2$y", TFLAG(TFLAG_SYNC));
815 1 EXPECT_PARSE_SEQ("\033[?2026;3$y", KEY_IGNORE);
816 1 EXPECT_PARSE_SEQ("\033[?2026;4$y", KEY_IGNORE);
817 1 EXPECT_PARSE_SEQ("\033[?2026;5$y", KEY_IGNORE);
818 1 EXPECT_PARSE_SEQ("\033[?0;1$y", KEY_IGNORE);
819 1 EXPECT_PARSE_SEQ("\033[?1036;2$y", TFLAG(TFLAG_META_ESC));
820 1 EXPECT_PARSE_SEQ("\033[?1039;2$y", TFLAG(TFLAG_ALT_ESC));
821 1 EXPECT_PARSE_SEQ("\033[?7;2$y", KEY_IGNORE);
822 1 EXPECT_PARSE_SEQ("\033[?25;2$y", KEY_IGNORE);
823 1 EXPECT_PARSE_SEQ("\033[?45;2$y", KEY_IGNORE);
824 1 EXPECT_PARSE_SEQ("\033[?67;2$y", KEY_IGNORE);
825 1 EXPECT_PARSE_SEQ("\033[?1049;2$y", KEY_IGNORE);
826 1 EXPECT_PARSE_SEQ("\033[?2004;2$y", KEY_IGNORE);
827
828 // Invalid, DECRPM-like sequences
829 1 EXPECT_PARSE_SEQ("\033[?9$y", KEY_IGNORE); // Too few params
830 1 EXPECT_PARSE_SEQ("\033[?1;2;3$y", KEY_IGNORE); // Too many params
831 1 EXPECT_PARSE_SEQ("\033[?1;2y", KEY_IGNORE); // No '$' intermediate byte
832 1 EXPECT_PARSE_SEQ("\033[1;2$y", KEY_IGNORE); // No '?' param prefix
833
834 // XTGETTCAP replies
835 1 EXPECT_PARSE_SEQN("\033P1+r626365\033\\", 11, TFLAG(TFLAG_BACK_COLOR_ERASE));
836 1 EXPECT_PARSE_SEQN("\033P1+r74736C=1B5D323B\033\\", 20, TFLAG(TFLAG_SET_WINDOW_TITLE));
837 1 EXPECT_PARSE_SEQN("\033P0+r\033\\", 5, KEY_IGNORE);
838 1 EXPECT_PARSE_SEQN("\033P0+rbbccdd\033\\", 11, KEY_IGNORE);
839
840 // DECRQSS replies
841 1 EXPECT_PARSE_SEQN("\033P1$r0;38:2::60:70:80;48:5:255m\033\\", 31, TFLAG(TFLAG_TRUE_COLOR | TFLAG_256_COLOR)); // SGR (xterm, foot)
842 1 EXPECT_PARSE_SEQN("\033P1$r0;38:2:60:70:80;48:5:255m\033\\", 30, TFLAG(TFLAG_TRUE_COLOR | TFLAG_256_COLOR)); // SGR (kitty)
843 1 EXPECT_PARSE_SEQN("\033P1$r0;zm\033\\", 9, KEY_IGNORE); // Invalid SGR-like
844 1 EXPECT_PARSE_SEQN("\033P1$r2 q\033\\", 8, KEY_IGNORE); // DECSCUSR 2 (cursor style)
845
846 // Kitty keyboard protocol query replies
847 1 EXPECT_PARSE_SEQ("\033[?5u", TFLAG(TFLAG_KITTY_KEYBOARD));
848 1 EXPECT_PARSE_SEQ("\033[?1u", TFLAG(TFLAG_KITTY_KEYBOARD));
849 1 EXPECT_PARSE_SEQ("\033[?0u", TFLAG(TFLAG_KITTY_KEYBOARD));
850
851 // Invalid, kitty-reply-like sequences
852 1 EXPECT_PARSE_SEQ("\033[?u", KEY_IGNORE); // Too few params (could be valid, in theory)
853 1 EXPECT_PARSE_SEQ("\033[?1;2u", KEY_IGNORE); // Too many params
854 1 EXPECT_PARSE_SEQ("\033[?1:2u", KEY_IGNORE); // Sub-params
855 1 EXPECT_PARSE_SEQ("\033[?1!u", KEY_IGNORE); // Intermediate '!' byte
856 1 }
857
858 1 static void test_term_parse_sequence2(TestContext *ctx)
859 {
860 1 static const struct {
861 char key_str[3];
862 char final_byte;
863 KeyCode key;
864 } templates[] = {
865 {"1", 'A', KEY_UP},
866 {"1", 'B', KEY_DOWN},
867 {"1", 'C', KEY_RIGHT},
868 {"1", 'D', KEY_LEFT},
869 {"1", 'E', KEY_BEGIN},
870 {"1", 'F', KEY_END},
871 {"1", 'H', KEY_HOME},
872 {"1", 'P', KEY_F1},
873 {"1", 'Q', KEY_F2},
874 {"1", 'R', KEY_F3}, // https://sw.kovidgoyal.net/kitty/keyboard-protocol/#:~:text=CSI%20R
875 {"1", 'S', KEY_F4},
876 {"2", '~', KEY_INSERT},
877 {"3", '~', KEY_DELETE},
878 {"5", '~', KEY_PAGE_UP},
879 {"6", '~', KEY_PAGE_DOWN},
880 {"11", '~', KEY_F1},
881 {"12", '~', KEY_F2},
882 {"13", '~', KEY_F3},
883 {"14", '~', KEY_F4},
884 {"15", '~', KEY_F5},
885 {"17", '~', KEY_F6},
886 {"18", '~', KEY_F7},
887 {"19", '~', KEY_F8},
888 {"20", '~', KEY_F9},
889 {"21", '~', KEY_F10},
890 {"23", '~', KEY_F11},
891 {"24", '~', KEY_F12},
892 {"25", '~', KEY_F13},
893 {"26", '~', KEY_F14},
894 {"28", '~', KEY_F15},
895 {"29", '~', KEY_F16},
896 {"31", '~', KEY_F17},
897 {"32", '~', KEY_F18},
898 {"33", '~', KEY_F19},
899 {"34", '~', KEY_F20},
900 };
901
902 1 static const struct {
903 char mod_str[4];
904 KeyCode mask;
905 } modifiers[] = {
906 {"0", KEY_IGNORE},
907 {"1", 0},
908 {"2", MOD_SHIFT},
909 {"3", MOD_META},
910 {"4", MOD_SHIFT | MOD_META},
911 {"5", MOD_CTRL},
912 {"6", MOD_SHIFT | MOD_CTRL},
913 {"7", MOD_META | MOD_CTRL},
914 {"8", MOD_SHIFT | MOD_META | MOD_CTRL},
915 {"9", MOD_SUPER},
916 {"10", MOD_SUPER | MOD_SHIFT},
917 {"11", MOD_SUPER | MOD_META},
918 {"12", MOD_SUPER | MOD_META | MOD_SHIFT},
919 {"13", MOD_SUPER | MOD_CTRL},
920 {"14", MOD_SUPER | MOD_CTRL | MOD_SHIFT},
921 {"15", MOD_SUPER | MOD_META | MOD_CTRL},
922 {"16", MOD_SUPER | MOD_META | MOD_CTRL | MOD_SHIFT},
923 {"17", MOD_HYPER},
924 {"18", MOD_HYPER | MOD_SHIFT},
925 {"255", MOD_HYPER | MOD_SUPER | MOD_META | MOD_CTRL},
926 {"256", MOD_MASK},
927 {"257", KEY_IGNORE},
928 {"400", KEY_IGNORE},
929 };
930
931 1 static_assert(KEY_IGNORE != 0);
932 1 static_assert((KEY_IGNORE & MOD_MASK) == 0);
933
934
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 1 times.
36 FOR_EACH_I(i, templates) {
935
2/2
✓ Branch 0 taken 805 times.
✓ Branch 1 taken 35 times.
840 FOR_EACH_I(j, modifiers) {
936 805 char seq[16];
937 1610 size_t seq_length = xsnprintf (
938 seq,
939 sizeof seq,
940 "\033[%s;%s%c",
941 805 templates[i].key_str,
942 805 modifiers[j].mod_str,
943 805 templates[i].final_byte
944 );
945 805 KeyCode key = 24;
946 805 ssize_t parsed_length = term_parse_sequence(seq, seq_length, &key);
947 805 KeyCode mods = modifiers[j].mask;
948
2/2
✓ Branch 0 taken 700 times.
✓ Branch 1 taken 105 times.
805 KeyCode expected_key = mods | (mods == KEY_IGNORE ? 0 : templates[i].key);
949 805 IEXPECT_EQ(parsed_length, seq_length);
950 805 IEXPECT_KEYCODE_EQ(key, expected_key, seq, seq_length);
951 // Truncated
952 805 key = 25;
953
2/2
✓ Branch 0 taken 5080 times.
✓ Branch 1 taken 805 times.
5885 for (size_t n = seq_length - 1; n != 0; n--) {
954 5080 parsed_length = term_parse_sequence(seq, n, &key);
955 5080 EXPECT_EQ(parsed_length, TPARSE_PARTIAL_MATCH);
956 5080 EXPECT_EQ(key, 25);
957 }
958 // Overlength
959 805 key = 26;
960 805 seq[seq_length++] = '~';
961 805 parsed_length = term_parse_sequence(seq, seq_length, &key);
962 805 IEXPECT_EQ(parsed_length, seq_length - 1);
963 805 IEXPECT_KEYCODE_EQ(key, expected_key, seq, seq_length);
964 }
965 }
966 1 }
967
968 1 static void test_rxvt_parse_key(TestContext *ctx)
969 {
970 1 static const struct {
971 char escape_sequence[8];
972 KeyCode key;
973 } templates[] = {
974 {"\033\033[2_", KEY_INSERT},
975 {"\033\033[3_", KEY_DELETE},
976 {"\033\033[5_", KEY_PAGE_UP},
977 {"\033\033[6_", KEY_PAGE_DOWN},
978 {"\033\033[7_", KEY_HOME},
979 {"\033\033[8_", KEY_END},
980 };
981
982 1 static const struct {
983 char ch;
984 KeyCode mask;
985 } modifiers[] = {
986 {'~', 0},
987 {'^', MOD_CTRL},
988 {'$', MOD_SHIFT},
989 {'@', MOD_SHIFT | MOD_CTRL},
990 };
991
992
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 FOR_EACH_I(i, templates) {
993
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 6 times.
30 FOR_EACH_I(j, modifiers) {
994 24 char seq[8];
995 24 memcpy(seq, templates[i].escape_sequence, 8);
996 24 ASSERT_EQ(seq[7], '\0');
997 24 char *underscore = strchr(seq, '_');
998 24 ASSERT_NONNULL(underscore);
999 24 *underscore = modifiers[j].ch;
1000 24 size_t seq_length = strlen(seq);
1001 24 KeyCode key;
1002
1003 // Double ESC
1004 24 ssize_t parsed_length = rxvt_parse_key(seq, seq_length, &key);
1005 24 EXPECT_EQ(parsed_length, seq_length);
1006 24 EXPECT_EQ(key, modifiers[j].mask | templates[i].key | MOD_META);
1007
1008 // Single ESC
1009 24 parsed_length = rxvt_parse_key(seq + 1, seq_length - 1, &key);
1010 24 EXPECT_EQ(parsed_length, seq_length - 1);
1011 24 EXPECT_EQ(key, modifiers[j].mask | templates[i].key);
1012
1013 // Truncated (double ESC)
1014
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 24 times.
120 for (size_t n = seq_length - 1; n != 0; n--) {
1015 96 key = 25;
1016 96 parsed_length = rxvt_parse_key(seq, n, &key);
1017 96 EXPECT_EQ(parsed_length, TPARSE_PARTIAL_MATCH);
1018 96 EXPECT_EQ(key, 25);
1019 }
1020
1021 // Truncated (single ESC)
1022
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 24 times.
96 for (size_t n = seq_length - 2; n != 0; n--) {
1023 72 key = 25;
1024 72 parsed_length = rxvt_parse_key(seq + 1, n, &key);
1025 72 EXPECT_EQ(parsed_length, TPARSE_PARTIAL_MATCH);
1026 72 EXPECT_EQ(key, 25);
1027 }
1028 }
1029 }
1030
1031 static const struct {
1032 char seq[6];
1033 uint8_t seq_length;
1034 int8_t expected_length;
1035 KeyCode expected_key;
1036 } tests[] = {
1037 {STRN("\033Oa"), 3, MOD_CTRL | KEY_UP},
1038 {STRN("\033Ob"), 3, MOD_CTRL | KEY_DOWN},
1039 {STRN("\033Oc"), 3, MOD_CTRL | KEY_RIGHT},
1040 {STRN("\033Od"), 3, MOD_CTRL | KEY_LEFT},
1041 {STRN("\033O"), TPARSE_PARTIAL_MATCH, 0},
1042 {STRN("\033[a"), 3, MOD_SHIFT | KEY_UP},
1043 {STRN("\033[b"), 3, MOD_SHIFT | KEY_DOWN},
1044 {STRN("\033[c"), 3, MOD_SHIFT | KEY_RIGHT},
1045 {STRN("\033[d"), 3, MOD_SHIFT | KEY_LEFT},
1046 {STRN("\033["), TPARSE_PARTIAL_MATCH, 0},
1047 {STRN("\033[1;5A"), 6, MOD_CTRL | KEY_UP},
1048 {STRN("\033[1;5"), TPARSE_PARTIAL_MATCH, 0},
1049 {STRN("\033\033[@"), 4, KEY_IGNORE},
1050 {STRN("\033\033["), TPARSE_PARTIAL_MATCH, 0},
1051 {STRN("\033\033"), TPARSE_PARTIAL_MATCH, 0},
1052 {STRN("\033"), TPARSE_PARTIAL_MATCH, 0},
1053 };
1054
1055
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
17 FOR_EACH_I(i, tests) {
1056 16 const char *seq = tests[i].seq;
1057 16 const size_t seq_length = tests[i].seq_length;
1058 16 KeyCode key = 0x18;
1059 16 ssize_t parsed_length = rxvt_parse_key(seq, seq_length, &key);
1060
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
16 KeyCode expected_key = (parsed_length > 0) ? tests[i].expected_key : 0x18;
1061 16 IEXPECT_EQ(parsed_length, tests[i].expected_length);
1062 16 IEXPECT_KEYCODE_EQ(key, expected_key, seq, seq_length);
1063 }
1064 1 }
1065
1066 1 static void test_linux_parse_key(TestContext *ctx)
1067 {
1068 1 static const struct {
1069 char seq[6];
1070 uint8_t seq_length;
1071 int8_t expected_length;
1072 KeyCode expected_key;
1073 } tests[] = {
1074 {STRN("\033[1;5A"), 6, MOD_CTRL | KEY_UP},
1075 {STRN("\033[[A"), 4, KEY_F1},
1076 {STRN("\033[[B"), 4, KEY_F2},
1077 {STRN("\033[[C"), 4, KEY_F3},
1078 {STRN("\033[[D"), 4, KEY_F4},
1079 {STRN("\033[[E"), 4, KEY_F5},
1080 {STRN("\033[[F"), 0, 0},
1081 {STRN("\033[["), TPARSE_PARTIAL_MATCH, 0},
1082 {STRN("\033["), TPARSE_PARTIAL_MATCH, 0},
1083 {STRN("\033"), TPARSE_PARTIAL_MATCH, 0},
1084 };
1085
1086
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 FOR_EACH_I(i, tests) {
1087 10 const char *seq = tests[i].seq;
1088 10 const size_t seq_length = tests[i].seq_length;
1089 10 KeyCode key = 0x18;
1090 10 ssize_t parsed_length = linux_parse_key(seq, seq_length, &key);
1091
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 KeyCode expected_key = (parsed_length > 0) ? tests[i].expected_key : 0x18;
1092 10 IEXPECT_EQ(parsed_length, tests[i].expected_length);
1093 10 IEXPECT_KEYCODE_EQ(key, expected_key, seq, seq_length);
1094 }
1095 1 }
1096
1097 1 static void test_keycode_to_string(TestContext *ctx)
1098 {
1099 1 static const struct {
1100 const char *str;
1101 KeyCode key;
1102 } tests[] = {
1103 {"a", 'a'},
1104 {"Z", 'Z'},
1105 {"0", '0'},
1106 {"{", '{'},
1107 {"space", KEY_SPACE},
1108 {"enter", KEY_ENTER},
1109 {"tab", KEY_TAB},
1110 {"escape", KEY_ESCAPE},
1111 {"backspace", KEY_BACKSPACE},
1112 {"insert", KEY_INSERT},
1113 {"delete", KEY_DELETE},
1114 {"home", KEY_HOME},
1115 {"end", KEY_END},
1116 {"pgup", KEY_PAGE_UP},
1117 {"pgdown", KEY_PAGE_DOWN},
1118 {"begin", KEY_BEGIN},
1119 {"left", KEY_LEFT},
1120 {"right", KEY_RIGHT},
1121 {"up", KEY_UP},
1122 {"down", KEY_DOWN},
1123 {"scrlock", KEY_SCROLL_LOCK},
1124 {"print", KEY_PRINT_SCREEN},
1125 {"pause", KEY_PAUSE},
1126 {"menu", KEY_MENU},
1127 {"C-a", MOD_CTRL | 'a'},
1128 {"C-s", MOD_CTRL | 's'},
1129 {"M-a", MOD_META | 'a'},
1130 {"M-S-{", MOD_META | MOD_SHIFT | '{'},
1131 {"C-S-a", MOD_CTRL | MOD_SHIFT | 'a'},
1132 {"M-S-a", MOD_META | MOD_SHIFT | 'a'},
1133 {"F1", KEY_F1},
1134 {"F5", KEY_F5},
1135 {"F12", KEY_F12},
1136 {"F13", KEY_F13},
1137 {"F16", KEY_F16},
1138 {"F20", KEY_F20},
1139 {"M-enter", MOD_META | KEY_ENTER},
1140 {"M-space", MOD_META | KEY_SPACE},
1141 {"S-tab", MOD_SHIFT | KEY_TAB},
1142 {"C-M-S-F12", MOD_CTRL | MOD_META | MOD_SHIFT | KEY_F12},
1143 {"C-M-F16", MOD_CTRL | MOD_META | KEY_F16},
1144 {"C-S-F20", MOD_CTRL | MOD_SHIFT | KEY_F20},
1145 {"C-M-S-up", MOD_CTRL | MOD_META | MOD_SHIFT | KEY_UP},
1146 {"C-M-delete", MOD_CTRL | MOD_META | KEY_DELETE},
1147 {"C-home", MOD_CTRL | KEY_HOME},
1148 {"s-space", MOD_SUPER | ' '},
1149 {"H-end", MOD_HYPER | KEY_END},
1150 #if __STDC_VERSION__ >= 201112L
1151 {u8"ก", 0x0E01},
1152 {u8"C-ก", MOD_CTRL | 0x0E01},
1153 {u8"M-Ф", MOD_META | 0x0424},
1154 #endif
1155 };
1156
1157 1 char buf[KEYCODE_STR_MAX];
1158 1 ASSERT_TRUE(sizeof(buf) >= sizeof("QUERY REPLY; 0xFFFFFFFF"));
1159
1160
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 1 times.
51 FOR_EACH_I(i, tests) {
1161 50 const char *str = tests[i].str;
1162 50 size_t len = strlen(str);
1163 50 ASSERT_TRUE(len < sizeof(buf));
1164 50 size_t buflen = keycode_to_string(tests[i].key, buf);
1165 50 IEXPECT_STREQ(buf, str);
1166 50 IEXPECT_EQ(buflen, len);
1167 50 IEXPECT_EQ(parse_key_string(str), tests[i].key);
1168 }
1169
1170 1 EXPECT_EQ(keycode_to_string(KEYCODE_DETECTED_PASTE, buf), 19);
1171 1 EXPECT_STREQ(buf, "INVALID; 0x00110025");
1172 1 EXPECT_EQ(keycode_to_string(KEYCODE_BRACKETED_PASTE, buf), 19);
1173 1 EXPECT_STREQ(buf, "INVALID; 0x00110026");
1174 1 EXPECT_EQ(keycode_to_string(KEY_IGNORE, buf), 19);
1175 1 EXPECT_STREQ(buf, "INVALID; 0x00110027");
1176 1 EXPECT_EQ(keycode_to_string(UINT32_MAX, buf), 23);
1177 1 EXPECT_STREQ(buf, "QUERY REPLY; 0xFFFFFFFF");
1178
1179 // These combos aren't round-trippable by the code above and can't end
1180 // up in real bindings, since the letters are normalized to lower case
1181 // by parse_key_string(). We still test them nevertheless; for the sake
1182 // of completeness and catching unexpected changes.
1183 1 EXPECT_EQ(keycode_to_string(MOD_CTRL | 'A', buf), 3);
1184 1 EXPECT_STREQ(buf, "C-A");
1185 1 EXPECT_EQ(keycode_to_string(MOD_CTRL | MOD_SHIFT | 'A', buf), 5);
1186 1 EXPECT_STREQ(buf, "C-S-A");
1187 1 EXPECT_EQ(keycode_to_string(MOD_META | MOD_SHIFT | 'A', buf), 5);
1188 1 EXPECT_STREQ(buf, "M-S-A");
1189 1 }
1190
1191 1 static void test_parse_key_string(TestContext *ctx)
1192 {
1193 1 EXPECT_EQ(parse_key_string("^I"), MOD_CTRL | 'i');
1194 1 EXPECT_EQ(parse_key_string("^M"), MOD_CTRL | 'm');
1195 1 EXPECT_EQ(parse_key_string("C-I"), MOD_CTRL | 'i');
1196 1 EXPECT_EQ(parse_key_string("C-M"), MOD_CTRL | 'm');
1197 1 EXPECT_EQ(parse_key_string("C-F1"), MOD_CTRL | KEY_F1);
1198 1 EXPECT_EQ(parse_key_string("C-M-S-F20"), MOD_CTRL | MOD_META | MOD_SHIFT | KEY_F20);
1199 1 EXPECT_EQ(parse_key_string("s-m"), MOD_SUPER | 'm');
1200 1 EXPECT_EQ(parse_key_string("H-y"), MOD_HYPER | 'y');
1201 1 EXPECT_EQ(parse_key_string("H-y"), MOD_HYPER | 'y');
1202 1 EXPECT_EQ(parse_key_string("H-S-z"), MOD_HYPER | MOD_SHIFT | 'z');
1203 1 EXPECT_EQ(parse_key_string("s-S-t"), MOD_SUPER | MOD_SHIFT | 't');
1204 1 EXPECT_EQ(parse_key_string("S-print"), MOD_SHIFT | KEY_PRINT_SCREEN);
1205 1 EXPECT_EQ(parse_key_string("C-S-scrlock"), MOD_CTRL | MOD_SHIFT | KEY_SCROLL_LOCK);
1206 1 EXPECT_EQ(parse_key_string("tab"), KEY_TAB);
1207 1 EXPECT_EQ(parse_key_string("enter"), KEY_ENTER);
1208 1 EXPECT_EQ(parse_key_string("space"), KEY_SPACE);
1209 1 EXPECT_EQ(parse_key_string("escape"), KEY_ESCAPE);
1210 1 EXPECT_EQ(parse_key_string("backspace"), KEY_BACKSPACE);
1211 1 EXPECT_EQ(parse_key_string("insert"), KEY_INSERT);
1212 1 EXPECT_EQ(parse_key_string("delete"), KEY_DELETE);
1213 1 EXPECT_EQ(parse_key_string("up"), KEY_UP);
1214 1 EXPECT_EQ(parse_key_string("down"), KEY_DOWN);
1215 1 EXPECT_EQ(parse_key_string("right"), KEY_RIGHT);
1216 1 EXPECT_EQ(parse_key_string("left"), KEY_LEFT);
1217 1 EXPECT_EQ(parse_key_string("begin"), KEY_BEGIN);
1218 1 EXPECT_EQ(parse_key_string("end"), KEY_END);
1219 1 EXPECT_EQ(parse_key_string("pgdown"), KEY_PAGE_DOWN);
1220 1 EXPECT_EQ(parse_key_string("home"), KEY_HOME);
1221 1 EXPECT_EQ(parse_key_string("pgup"), KEY_PAGE_UP);
1222 1 EXPECT_EQ(parse_key_string("scrlock"), KEY_SCROLL_LOCK);
1223 1 EXPECT_EQ(parse_key_string("print"), KEY_PRINT_SCREEN);
1224 1 EXPECT_EQ(parse_key_string("pause"), KEY_PAUSE);
1225 1 EXPECT_EQ(parse_key_string("menu"), KEY_MENU);
1226 1 EXPECT_EQ(parse_key_string("f1"), KEY_F1);
1227 1 EXPECT_EQ(parse_key_string("f2"), KEY_F2);
1228 1 EXPECT_EQ(parse_key_string("f3"), KEY_F3);
1229 1 EXPECT_EQ(parse_key_string("f4"), KEY_F4);
1230 1 EXPECT_EQ(parse_key_string("f5"), KEY_F5);
1231 1 EXPECT_EQ(parse_key_string("f6"), KEY_F6);
1232 1 EXPECT_EQ(parse_key_string("f7"), KEY_F7);
1233 1 EXPECT_EQ(parse_key_string("f8"), KEY_F8);
1234 1 EXPECT_EQ(parse_key_string("f9"), KEY_F9);
1235 1 EXPECT_EQ(parse_key_string("f10"), KEY_F10);
1236 1 EXPECT_EQ(parse_key_string("f11"), KEY_F11);
1237 1 EXPECT_EQ(parse_key_string("f12"), KEY_F12);
1238 1 EXPECT_EQ(parse_key_string("f13"), KEY_F13);
1239 1 EXPECT_EQ(parse_key_string("f14"), KEY_F14);
1240 1 EXPECT_EQ(parse_key_string("f15"), KEY_F15);
1241 1 EXPECT_EQ(parse_key_string("f16"), KEY_F16);
1242 1 EXPECT_EQ(parse_key_string("f17"), KEY_F17);
1243 1 EXPECT_EQ(parse_key_string("f18"), KEY_F18);
1244 1 EXPECT_EQ(parse_key_string("f19"), KEY_F19);
1245 1 EXPECT_EQ(parse_key_string("f20"), KEY_F20);
1246
1247 1 EXPECT_EQ(parse_key_string("C-"), KEY_NONE);
1248 1 EXPECT_EQ(parse_key_string("C-M-"), KEY_NONE);
1249 1 EXPECT_EQ(parse_key_string("paste"), KEY_NONE);
1250 1 EXPECT_EQ(parse_key_string("???"), KEY_NONE);
1251 1 EXPECT_EQ(parse_key_string("F0"), KEY_NONE);
1252 1 EXPECT_EQ(parse_key_string("F21"), KEY_NONE);
1253 1 EXPECT_EQ(parse_key_string("F01"), KEY_NONE);
1254 1 EXPECT_EQ(parse_key_string("\t"), KEY_NONE);
1255 1 EXPECT_EQ(parse_key_string("\n"), KEY_NONE);
1256 1 EXPECT_EQ(parse_key_string("\r"), KEY_NONE);
1257 1 EXPECT_EQ(parse_key_string("\x1f"), KEY_NONE);
1258 1 EXPECT_EQ(parse_key_string("\x7f"), KEY_NONE);
1259 1 EXPECT_EQ(parse_key_string("C-\t"), KEY_NONE);
1260 1 EXPECT_EQ(parse_key_string("C-\r"), KEY_NONE);
1261 1 EXPECT_EQ(parse_key_string("C-\x7f"), KEY_NONE);
1262
1263 // Special cases for normalization:
1264 1 EXPECT_EQ(parse_key_string("C-A"), MOD_CTRL | 'a');
1265 1 EXPECT_EQ(parse_key_string("C-S-A"), MOD_CTRL | MOD_SHIFT | 'a');
1266 1 EXPECT_EQ(parse_key_string("M-A"), MOD_META | MOD_SHIFT | 'a');
1267 1 EXPECT_EQ(parse_key_string("C-?"), MOD_CTRL | '?');
1268 1 EXPECT_EQ(parse_key_string("C-H"), MOD_CTRL | 'h');
1269 1 EXPECT_EQ(parse_key_string("M-C-?"), MOD_META | MOD_CTRL | '?');
1270 1 EXPECT_EQ(parse_key_string("M-C-H"), MOD_META | MOD_CTRL | 'h');
1271 1 }
1272
1273 26 static bool clear_obuf(TermOutputBuffer *obuf)
1274 {
1275
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if (unlikely(obuf_avail(obuf) <= 8)) {
1276 return false;
1277 }
1278 26 memset(obuf->buf, '\0', obuf->count + 8);
1279 26 obuf->count = 0;
1280 26 obuf->x = 0;
1281 26 return true;
1282 }
1283
1284 1 static void test_term_init(TestContext *ctx)
1285 {
1286 1 const TermFeatureFlags expected_xterm_flags =
1287 TFLAG_256_COLOR |
1288 TFLAG_16_COLOR |
1289 TFLAG_8_COLOR |
1290 TFLAG_BACK_COLOR_ERASE |
1291 TFLAG_SET_WINDOW_TITLE |
1292 TFLAG_OSC52_COPY
1293 ;
1294
1295 1 Terminal term;
1296 1 term_init(&term, "xterm-256color", NULL);
1297 1 EXPECT_EQ(term.width, 80);
1298 1 EXPECT_EQ(term.height, 24);
1299 1 EXPECT_EQ(term.ncv_attributes, 0);
1300 1 EXPECT_PTREQ(term.parse_input, term_parse_sequence);
1301 1 EXPECT_EQ(term.features, expected_xterm_flags);
1302
1303 1 term_init(&term, "ansi", NULL);
1304 1 EXPECT_EQ(term.width, 80);
1305 1 EXPECT_EQ(term.height, 24);
1306 1 EXPECT_EQ(term.ncv_attributes, ATTR_UNDERLINE);
1307 1 EXPECT_PTREQ(term.parse_input, term_parse_sequence);
1308 1 EXPECT_EQ(term.features, TFLAG_8_COLOR);
1309
1310 1 term_init(&term, "ansi-m", NULL);
1311 1 EXPECT_EQ(term.width, 80);
1312 1 EXPECT_EQ(term.height, 24);
1313 1 EXPECT_EQ(term.ncv_attributes, 0);
1314 1 EXPECT_PTREQ(term.parse_input, term_parse_sequence);
1315 1 EXPECT_EQ(term.features, 0);
1316 1 }
1317
1318 1 static void test_term_put_str(TestContext *ctx)
1319 {
1320 1 Terminal term = {.width = 80, .height = 24};
1321 1 TermOutputBuffer *obuf = &term.obuf;
1322 1 term_output_init(obuf);
1323 1 ASSERT_NONNULL(obuf->buf);
1324
1325 // Fill start of buffer with zeroes, to allow using EXPECT_STREQ() below
1326 1 ASSERT_TRUE(256 <= TERM_OUTBUF_SIZE);
1327 1 memset(obuf->buf, 0, 256);
1328
1329 1 obuf->width = 0;
1330 1 term_put_str(obuf, "this should write nothing because obuf->width == 0");
1331 1 EXPECT_EQ(obuf->count, 0);
1332 1 EXPECT_EQ(obuf->x, 0);
1333
1334 1 term_output_reset(&term, 0, 80, 0);
1335 1 EXPECT_EQ(obuf->tab_mode, TAB_CONTROL);
1336 1 EXPECT_EQ(obuf->tab_width, 8);
1337 1 EXPECT_EQ(obuf->x, 0);
1338 1 EXPECT_EQ(obuf->width, 80);
1339 1 EXPECT_EQ(obuf->scroll_x, 0);
1340 1 EXPECT_EQ(term.width, 80);
1341 1 EXPECT_EQ(obuf->can_clear, true);
1342
1343 1 term_put_str(obuf, "1\xF0\x9F\xA7\xB2 \t xyz \t\r \xC2\xB6");
1344 1 EXPECT_EQ(obuf->count, 20);
1345 1 EXPECT_EQ(obuf->x, 17);
1346 1 EXPECT_STREQ(obuf->buf, "1\xF0\x9F\xA7\xB2 ^I xyz ^I^M \xC2\xB6");
1347
1348 1 EXPECT_TRUE(term_put_char(obuf, 0x10FFFF));
1349 1 EXPECT_EQ(obuf->count, 24);
1350 1 EXPECT_EQ(obuf->x, 21);
1351 1 EXPECT_STREQ(obuf->buf + 20, "<" "??" ">");
1352 1 ASSERT_TRUE(clear_obuf(obuf));
1353
1354 1 term_output_free(obuf);
1355 1 }
1356
1357 1 static void test_term_clear_eol(TestContext *ctx)
1358 {
1359 1 Terminal term = {.width = 80, .height = 24};
1360 1 TermOutputBuffer *obuf = &term.obuf;
1361 1 term_output_init(obuf);
1362
1363 // BCE with non-default bg
1364 1 term.features = TFLAG_BACK_COLOR_ERASE;
1365 1 obuf->style = (TermStyle){.bg = COLOR_RED};
1366 1 term_output_reset(&term, 0, 80, 0);
1367 1 EXPECT_EQ(term_clear_eol(&term), TERM_CLEAR_EOL_USED_EL);
1368 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[K", 3);
1369 1 EXPECT_EQ(obuf->x, 80);
1370 1 ASSERT_TRUE(clear_obuf(obuf));
1371
1372 // No BCE, but with REP
1373 1 term.features = TFLAG_ECMA48_REPEAT;
1374 1 obuf->style = (TermStyle){.bg = COLOR_RED};
1375 1 term_output_reset(&term, 0, 40, 0);
1376 1 EXPECT_EQ(term_clear_eol(&term), -40);
1377 1 EXPECT_MEMEQ(obuf->buf, obuf->count, " \033[39b", 6);
1378 1 EXPECT_EQ(obuf->x, 40);
1379 1 ASSERT_TRUE(clear_obuf(obuf));
1380
1381 // No BCE with non-default bg
1382 1 term.features = 0;
1383 1 obuf->style = (TermStyle){.bg = COLOR_RED};
1384 1 term_output_reset(&term, 0, 80, 0);
1385 1 EXPECT_EQ(term_clear_eol(&term), 80);
1386 1 EXPECT_EQ(obuf->count, 80);
1387 1 EXPECT_EQ(obuf->x, 80);
1388 1 EXPECT_EQ(obuf->buf[0], ' ');
1389 1 EXPECT_EQ(obuf->buf[79], ' ');
1390 1 ASSERT_TRUE(clear_obuf(obuf));
1391
1392 // No BCE with default bg/attrs
1393 1 term.features = 0;
1394 1 obuf->style = (TermStyle){.bg = COLOR_DEFAULT};
1395 1 term_output_reset(&term, 0, 80, 0);
1396 1 EXPECT_EQ(term_clear_eol(&term), TERM_CLEAR_EOL_USED_EL);
1397 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[K", 3);
1398 1 EXPECT_EQ(obuf->x, 80);
1399 1 ASSERT_TRUE(clear_obuf(obuf));
1400
1401 // No BCE with ATTR_REVERSE
1402 1 term.features = 0;
1403 1 obuf->style = (TermStyle){.bg = COLOR_DEFAULT, .attr = ATTR_REVERSE};
1404 1 term_output_reset(&term, 0, 80, 0);
1405 1 EXPECT_EQ(term_clear_eol(&term), 80);
1406 1 EXPECT_EQ(obuf->count, 80);
1407 1 EXPECT_EQ(obuf->x, 80);
1408 1 ASSERT_TRUE(clear_obuf(obuf));
1409
1410 // x >= scroll_x + width
1411 1 term_output_reset(&term, 0, 20, 10);
1412 1 EXPECT_EQ(obuf->width, 20);
1413 1 EXPECT_EQ(obuf->scroll_x, 10);
1414 1 obuf->x = 30;
1415 1 EXPECT_EQ(term_clear_eol(&term), 0);
1416 1 EXPECT_EQ(obuf->count, 0);
1417 1 EXPECT_EQ(obuf->x, 30);
1418 1 ASSERT_TRUE(clear_obuf(obuf));
1419
1420 1 term_output_free(obuf);
1421 1 }
1422
1423 1 static void test_term_move_cursor(TestContext *ctx)
1424 {
1425 1 TermOutputBuffer obuf;
1426 1 term_output_init(&obuf);
1427 1 term_move_cursor(&obuf, 12, 5);
1428 1 EXPECT_MEMEQ(obuf.buf, obuf.count, "\033[6;13H", 7);
1429 1 EXPECT_EQ(obuf.x, 0);
1430 1 ASSERT_TRUE(clear_obuf(&obuf));
1431
1432 1 term_move_cursor(&obuf, 0, 22);
1433 1 EXPECT_MEMEQ(obuf.buf, obuf.count, "\033[23H", 5);
1434 1 EXPECT_EQ(obuf.x, 0);
1435 1 ASSERT_TRUE(clear_obuf(&obuf));
1436
1437 1 term_output_free(&obuf);
1438 1 }
1439
1440 1 static void test_term_set_bytes(TestContext *ctx)
1441 {
1442 1 Terminal term = {
1443 .width = 80,
1444 .height = 24,
1445 .features = TFLAG_ECMA48_REPEAT,
1446 };
1447
1448 1 TermOutputBuffer *obuf = &term.obuf;
1449 1 term_output_init(obuf);
1450 1 ASSERT_TRUE(clear_obuf(obuf));
1451 1 term_output_reset(&term, 0, 80, 0);
1452
1453 1 EXPECT_EQ(term_set_bytes(&term, 'x', 40), TERM_SET_BYTES_REP);
1454 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "x\033[39b", 6);
1455 1 EXPECT_EQ(obuf->x, 40);
1456 1 ASSERT_TRUE(clear_obuf(obuf));
1457
1458 1 const size_t repmin = ECMA48_REP_MIN - 1;
1459 1 EXPECT_EQ(repmin, 5);
1460 1 EXPECT_EQ(term_set_bytes(&term, '-', repmin), TERM_SET_BYTES_MEMSET);
1461 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "-----", repmin);
1462 1 EXPECT_EQ(obuf->x, repmin);
1463 1 ASSERT_TRUE(clear_obuf(obuf));
1464
1465 1 EXPECT_EQ(term_set_bytes(&term, '\n', 8), TERM_SET_BYTES_MEMSET);
1466 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\n\n\n\n\n\n\n\n", 8);
1467 1 EXPECT_EQ(obuf->x, 8);
1468 1 ASSERT_TRUE(clear_obuf(obuf));
1469
1470 1 term_output_free(obuf);
1471 1 }
1472
1473 1 static void test_term_set_style(TestContext *ctx)
1474 {
1475 1 Terminal term = {.features = TFLAG_8_COLOR};
1476 1 term_init(&term, "tmux", "truecolor");
1477 1 EXPECT_TRUE(term.features & TFLAG_TRUE_COLOR);
1478 1 EXPECT_EQ(term.ncv_attributes, 0);
1479
1480 1 TermOutputBuffer *obuf = &term.obuf;
1481 1 term_output_init(obuf);
1482 1 ASSERT_TRUE(TERM_OUTBUF_SIZE >= 64);
1483 1 memset(obuf->buf, '?', 64);
1484
1485 1 TermStyle style = {
1486 .fg = COLOR_RED,
1487 .bg = COLOR_YELLOW,
1488 .attr = ATTR_BOLD | ATTR_REVERSE,
1489 };
1490
1491 1 term_set_style(&term, style);
1492 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;1;7;31;43m", 14);
1493 1 EXPECT_EQ(obuf->x, 0);
1494 1 ASSERT_TRUE(clear_obuf(obuf));
1495
1496 1 style.attr = 0;
1497 1 style.fg = COLOR_RGB(0x12ef46);
1498 1 term_set_style(&term, style);
1499 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;38;2;18;239;70;43m", 22);
1500 1 EXPECT_EQ(obuf->x, 0);
1501 1 ASSERT_TRUE(clear_obuf(obuf));
1502
1503 1 style.fg = 144;
1504 1 style.bg = COLOR_DEFAULT;
1505 1 term_set_style(&term, style);
1506 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;38;5;144m", 13);
1507 1 EXPECT_EQ(obuf->x, 0);
1508 1 ASSERT_TRUE(clear_obuf(obuf));
1509
1510 1 style.fg = COLOR_GRAY;
1511 1 style.bg = COLOR_RGB(0x00b91f);
1512 1 term_set_style(&term, style);
1513 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;37;48;2;0;185;31m", 21);
1514 1 EXPECT_EQ(obuf->x, 0);
1515 1 ASSERT_TRUE(clear_obuf(obuf));
1516
1517 1 style.fg = COLOR_WHITE;
1518 1 style.bg = COLOR_DARKGRAY;
1519 1 style.attr = ATTR_ITALIC;
1520 1 term_set_style(&term, style);
1521 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;3;97;100m", 13);
1522 1 EXPECT_EQ(obuf->x, 0);
1523 1 ASSERT_TRUE(clear_obuf(obuf));
1524
1525 // Ensure longest sequence doesn't trigger assertion
1526 1 static const char longest[] =
1527 "\033[0;"
1528 "1;2;3;4;5;7;8;9;"
1529 "38;2;100;101;199;"
1530 "48;2;200;202;255m"
1531 ;
1532 1 const size_t n = sizeof(longest) - 1;
1533 1 ASSERT_EQ(n, 54);
1534 1 style.fg = COLOR_RGB(0x6465C7);
1535 1 style.bg = COLOR_RGB(0xC8CAFF);
1536 1 style.attr = ~0u;
1537 1 term_set_style(&term, style);
1538 1 EXPECT_MEMEQ(obuf->buf, obuf->count, longest, n);
1539 1 EXPECT_EQ(obuf->x, 0);
1540 1 ASSERT_TRUE(clear_obuf(obuf));
1541
1542 1 style.fg = COLOR_DEFAULT;
1543 1 style.bg = COLOR_DEFAULT;
1544 1 style.attr = ATTR_REVERSE | ATTR_DIM | ATTR_UNDERLINE | ATTR_KEEP;
1545 1 term.ncv_attributes = ATTR_DIM | ATTR_STRIKETHROUGH;
1546 1 term_set_style(&term, style);
1547 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;2;4;7m", 10);
1548 1 EXPECT_EQ(obuf->x, 0);
1549 1 style.attr &= ~ATTR_KEEP;
1550 1 EXPECT_TRUE(same_style(&obuf->style, &style));
1551 1 ASSERT_TRUE(clear_obuf(obuf));
1552
1553 1 style.fg = COLOR_BLUE;
1554 1 term_set_style(&term, style);
1555 1 EXPECT_MEMEQ(obuf->buf, obuf->count, "\033[0;4;7;34m", 11);
1556 1 EXPECT_EQ(obuf->x, 0);
1557 1 style.attr &= ~ATTR_DIM;
1558 1 EXPECT_TRUE(same_style(&obuf->style, &style));
1559 1 ASSERT_TRUE(clear_obuf(obuf));
1560
1561 1 term_output_free(obuf);
1562 1 }
1563
1564 1 static void test_term_osc52_copy(TestContext *ctx)
1565 {
1566 1 TermOutputBuffer obuf;
1567 1 term_output_init(&obuf);
1568
1569 1 EXPECT_TRUE(term_osc52_copy(&obuf, STRN("foobar"), true, true));
1570 1 EXPECT_MEMEQ(obuf.buf, obuf.count, "\033]52;pc;Zm9vYmFy\033\\", 18);
1571 1 EXPECT_EQ(obuf.x, 0);
1572 1 ASSERT_TRUE(clear_obuf(&obuf));
1573
1574 1 EXPECT_TRUE(term_osc52_copy(&obuf, STRN("\xF0\x9F\xA5\xA3"), false, true));
1575 1 EXPECT_MEMEQ(obuf.buf, obuf.count, "\033]52;p;8J+low==\033\\", 17);
1576 1 EXPECT_EQ(obuf.x, 0);
1577 1 ASSERT_TRUE(clear_obuf(&obuf));
1578
1579 1 EXPECT_TRUE(term_osc52_copy(&obuf, STRN(""), true, false));
1580 1 EXPECT_MEMEQ(obuf.buf, obuf.count, "\033]52;c;\033\\", 9);
1581 1 EXPECT_EQ(obuf.x, 0);
1582 1 ASSERT_TRUE(clear_obuf(&obuf));
1583
1584 1 term_output_free(&obuf);
1585 1 }
1586
1587 1 static void test_term_set_cursor_style(TestContext *ctx)
1588 {
1589 1 Terminal term = {
1590 .width = 80,
1591 .height = 24,
1592 };
1593
1594 1 TermCursorStyle style = {
1595 .type = CURSOR_STEADY_BAR,
1596 .color = COLOR_RGB(0x22AACC),
1597 };
1598
1599 1 static const char expected[] = "\033[6 q\033]12;rgb:22/aa/cc\033\\";
1600 1 size_t expected_len = sizeof(expected) - 1;
1601 1 ASSERT_EQ(expected_len, 24);
1602
1603 1 TermOutputBuffer *obuf = &term.obuf;
1604 1 term_output_init(obuf);
1605 1 memset(obuf->buf, '@', expected_len + 16);
1606
1607 1 term_set_cursor_style(&term, style);
1608 1 EXPECT_MEMEQ(obuf->buf, obuf->count, expected, expected_len);
1609 1 EXPECT_EQ(obuf->x, 0);
1610 1 EXPECT_EQ(obuf->cursor_style.type, style.type);
1611 1 EXPECT_EQ(obuf->cursor_style.color, style.color);
1612 1 ASSERT_TRUE(clear_obuf(obuf));
1613
1614 1 term_output_free(obuf);
1615 1 }
1616
1617 1 static void test_term_restore_cursor_style(TestContext *ctx)
1618 {
1619 1 Terminal term = {
1620 .width = 80,
1621 .height = 24,
1622 };
1623
1624 1 static const char expected[] = "\033[0 q\033]112\033\\";
1625 1 size_t expected_len = sizeof(expected) - 1;
1626 1 ASSERT_EQ(expected_len, 12);
1627
1628 1 TermOutputBuffer *obuf = &term.obuf;
1629 1 term_output_init(obuf);
1630 1 memset(obuf->buf, '@', expected_len + 16);
1631
1632 1 term_restore_cursor_style(&term);
1633 1 EXPECT_MEMEQ(obuf->buf, obuf->count, expected, expected_len);
1634 1 EXPECT_EQ(obuf->x, 0);
1635 1 ASSERT_TRUE(clear_obuf(obuf));
1636
1637 1 term_output_free(obuf);
1638 1 }
1639
1640 1 static void test_term_begin_sync_update(TestContext *ctx)
1641 {
1642 1 Terminal term;
1643 1 term_init(&term, "xterm-kitty", NULL);
1644 1 EXPECT_TRUE(term.features & TFLAG_SYNC);
1645
1646 1 static const char expected[] =
1647 "\033[?2026h"
1648 "\033[?1049h"
1649 "\033[?25l"
1650 "\033[?25h"
1651 "\033[?1049l"
1652 "\033[?2026l"
1653 ;
1654
1655 1 TermOutputBuffer *obuf = &term.obuf;
1656 1 term_output_init(obuf);
1657 1 memset(obuf->buf, '.', 128);
1658
1659 1 term_begin_sync_update(&term);
1660 1 term_use_alt_screen_buffer(&term);
1661 1 term_hide_cursor(&term);
1662 1 term_show_cursor(&term);
1663 1 term_use_normal_screen_buffer(&term);
1664 1 term_end_sync_update(&term);
1665
1666 1 EXPECT_MEMEQ(obuf->buf, obuf->count, expected, sizeof(expected) - 1);
1667 1 EXPECT_EQ(obuf->x, 0);
1668
1669 1 term_output_free(obuf);
1670 1 }
1671
1672 1 static void test_term_put_level_1_queries(TestContext *ctx)
1673 {
1674 1 enum {
1675 // Short aliases for TermFeatureFlags:
1676 BCE = TFLAG_BACK_COLOR_ERASE,
1677 TITLE = TFLAG_SET_WINDOW_TITLE,
1678 OSC52 = TFLAG_OSC52_COPY,
1679 C8 = TFLAG_8_COLOR,
1680 C16 = TFLAG_16_COLOR | C8,
1681 C256 = TFLAG_256_COLOR | C16,
1682 };
1683
1684 1 Terminal term;
1685 1 TermOutputBuffer *obuf = &term.obuf;
1686 1 term_output_init(obuf);
1687 1 term_init(&term, "xterm-256color", NULL);
1688 1 EXPECT_UINT_EQ(term.features, (C256 | BCE | TITLE | OSC52));
1689 1 EXPECT_EQ(obuf->count, 0);
1690
1691 // Basic level 1 queries
1692 1 static const char level1[] = "\033[c";
1693 1 term_put_initial_queries(&term, 1);
1694 1 EXPECT_MEMEQ(obuf->buf, obuf->count, level1, sizeof(level1) - 1);
1695
1696 // All queries (forced by emit_all=true)
1697 1 static const char full[] =
1698 // term_put_initial_queries()
1699 "\033[c"
1700 // term_put_level_2_queries()
1701 "\033[>0q"
1702 "\033[>c"
1703 "\033[=c"
1704 "\033[?u"
1705 "\033[?1036$p"
1706 "\033[?1039$p"
1707 "\033[?2026$p"
1708 // term_put_level_2_queries() debug
1709 "\033[?4m"
1710 "\033[?7$p"
1711 "\033[?25$p"
1712 "\033[?45$p"
1713 "\033[?67$p"
1714 "\033[?1049$p"
1715 "\033[?2004$p"
1716 "\033[18t"
1717 // term_put_level_3_queries()
1718 "\033[0;38;2;60;70;80;48;5;255m"
1719 "\033P$qm\033\\"
1720 "\033[0m"
1721 "\033P+q626365\033\\"
1722 "\033P+q726570\033\\"
1723 "\033P+q74736C\033\\"
1724 "\033P+q4D73\033\\"
1725 // term_put_level_3_queries() debug
1726 "\033P$q q\033\\"
1727 ;
1728
1729 1 obuf->count = 0;
1730 1 term_put_initial_queries(&term, 6);
1731 1 EXPECT_MEMEQ(obuf->buf, obuf->count, full, sizeof(full) - 1);
1732
1733 1 EXPECT_EQ(obuf->scroll_x, 0);
1734 1 EXPECT_EQ(obuf->x, 0);
1735 1 term_output_free(obuf);
1736 1 }
1737
1738 1 static void test_is_newly_detected_feature(TestContext *ctx)
1739 {
1740 1 EXPECT_FALSE(is_newly_detected_feature(1, 1, 1));
1741 1 EXPECT_FALSE(is_newly_detected_feature(1, 2, 1));
1742 1 EXPECT_FALSE(is_newly_detected_feature(3, 1, 1));
1743 1 EXPECT_FALSE(is_newly_detected_feature(1, 3, 1));
1744 1 EXPECT_FALSE(is_newly_detected_feature(0, 6, 1));
1745 1 EXPECT_TRUE(is_newly_detected_feature(0, 1, 1));
1746 1 EXPECT_TRUE(is_newly_detected_feature(2, 1, 1));
1747 1 EXPECT_TRUE(is_newly_detected_feature(3, 4, 4));
1748 1 }
1749
1750 static const TestEntry tests[] = {
1751 TEST(test_parse_rgb),
1752 TEST(test_parse_term_style),
1753 TEST(test_color_to_nearest),
1754 TEST(test_color_to_str),
1755 TEST(test_term_style_to_string),
1756 TEST(test_cursor_mode_from_str),
1757 TEST(test_cursor_type_from_str),
1758 TEST(test_cursor_color_from_str),
1759 TEST(test_cursor_color_to_str),
1760 TEST(test_same_cursor),
1761 TEST(test_term_parse_csi_params),
1762 TEST(test_term_parse_sequence),
1763 TEST(test_term_parse_sequence2),
1764 TEST(test_rxvt_parse_key),
1765 TEST(test_linux_parse_key),
1766 TEST(test_keycode_to_string),
1767 TEST(test_parse_key_string),
1768 TEST(test_term_init),
1769 TEST(test_term_put_str),
1770 TEST(test_term_clear_eol),
1771 TEST(test_term_move_cursor),
1772 TEST(test_term_set_bytes),
1773 TEST(test_term_set_style),
1774 TEST(test_term_osc52_copy),
1775 TEST(test_term_set_cursor_style),
1776 TEST(test_term_restore_cursor_style),
1777 TEST(test_term_begin_sync_update),
1778 TEST(test_term_put_level_1_queries),
1779 TEST(test_is_newly_detected_feature),
1780 };
1781
1782 const TestGroup terminal_tests = TEST_GROUP(tests);
1783