summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-15 12:30:14 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-15 12:30:14 +0300
commit7ed78a31debb0250bae16d8b4098a4689f29bb60 (patch)
treeeda4e03a9c51140f78ef6e8c9f73ebcd0d8eca8b /tests
parent5f03033b0e211f9ed13e7a1351f9a8f9011ab665 (diff)
Take the terminal name from term_options.name
The core hard-coded "wsap_term" for XTVERSION, the XTGETTCAP TN and name default, and the OSC 9 notification title. A headless library should take its identity from the host. term_options.name sets the name. NULL or "" selects "term". term_new() copies it and returns NULL unless it is 1 to 63 printable ASCII bytes, since DCS replies echo it. A DCS +p still changes the TN value, and the XTVERSION and notification defaults keep the host name. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/dcs_test.c53
-rw-r--r--tests/model.c6
-rw-r--r--tests/model.h5
-rw-r--r--tests/model_row_test.c2
-rw-r--r--tests/model_test.c2
-rw-r--r--tests/osc_test.c32
-rw-r--r--tests/term_test.c27
7 files changed, 116 insertions, 11 deletions
diff --git a/tests/dcs_test.c b/tests/dcs_test.c
index 0e67f78..d9455be 100644
--- a/tests/dcs_test.c
+++ b/tests/dcs_test.c
@@ -176,8 +176,8 @@ static void test_xtgettcap(void)
{
struct term *t = new_term(80, 24);
assert_tcap(t, "kind", "\033[1;2B");
- assert_tcap(t, "TN", "wsap_term");
- assert_tcap(t, "name", "wsap_term");
+ assert_tcap(t, "TN", "term");
+ assert_tcap(t, "name", "term");
assert_tcap(t, "Co", "16777216");
assert_tcap(t, "colors", "16777216");
assert_tcap(t, "RGB", "8/8/8");
@@ -230,7 +230,7 @@ static void test_xtgettcap_multi(void)
snprintf(req, sizeof(req), "\033P+q%s;%s\033\\", hk, htn);
snprintf(want, sizeof(want), "\033P1+r%s=%s;%s=%s\033\\",
- hk, hv, htn, "777361705f7465726d");
+ hk, hv, htn, "7465726d");
response_len = 0;
write_seq(t, req);
expect_response(want);
@@ -383,7 +383,51 @@ static void test_xtversion(void)
struct term *t = new_term(80, 24);
response_len = 0;
write_seq(t, "\033[>0q");
- expect_response("\033P>|wsap_term\033\\");
+ expect_response("\033P>|term\033\\");
+ term_free(t);
+}
+
+/* term_options.name feeds XTVERSION and the TN and name capabilities. */
+static void test_host_name(void)
+{
+ struct term_options opt = {
+ .size = { .x = 80, .y = 24 },
+ .scrollback = TEST_SCROLLBACK,
+ .name = "myterm",
+ .callbacks = { .reply = respond },
+ };
+ struct term *t = term_new(&opt);
+ assert(t);
+ response_len = 0;
+ write_seq(t, "\033[>0q");
+ expect_response("\033P>|myterm\033\\");
+ assert_tcap(t, "TN", "myterm");
+ assert_tcap(t, "name", "myterm");
+
+ /* A DCS +p sets the capability value. The XTVERSION name does not change. */
+ response_len = 0;
+ write_seq(t, "\033P+p6f74686572\033\\");
+ expect_no_response();
+ assert_tcap(t, "TN", "other");
+ response_len = 0;
+ write_seq(t, "\033[>0q");
+ expect_response("\033P>|myterm\033\\");
+ term_free(t);
+
+ /* NULL and "" both select the default. */
+ opt.name = "";
+ t = term_new(&opt);
+ assert(t);
+ response_len = 0;
+ write_seq(t, "\033[>0q");
+ expect_response("\033P>|term\033\\");
+ term_free(t);
+ opt.name = NULL;
+ t = term_new(&opt);
+ assert(t);
+ response_len = 0;
+ write_seq(t, "\033[>0q");
+ expect_response("\033P>|term\033\\");
term_free(t);
}
@@ -467,6 +511,7 @@ int main(void)
test_synchronized_output();
test_ignored_dcs();
test_xtversion();
+ test_host_name();
test_bel_terminator();
test_dcs_long_query();
test_dcs_abort_c0();
diff --git a/tests/model.c b/tests/model.c
index 8dead6c..b538ba1 100644
--- a/tests/model.c
+++ b/tests/model.c
@@ -72,6 +72,7 @@ enum { GROUND, ESC, CSI, STR, STR_ESC, CHARSET, HASH, DOCS };
enum { OSC, DCS, IGNORE };
struct model {
+ char name[64]; /* XTVERSION reply */
int cols, rows, limit;
struct row **scr;
struct row **hist;
@@ -1625,7 +1626,7 @@ static void csi_dispatch(struct model *m, unsigned char final)
break;
case 'q':
if (m->prefix == '>' && !m->inter && (!m->nparam || !m->param[0])) {
- reply(m, "\033P>|wsap_term\033\\");
+ reply(m, "\033P>|%s\033\\", m->name);
} else if (!m->prefix && m->inter == ' ') {
int ps = m->nparam ? m->param[0] : 0;
if (ps == 0) {
@@ -1971,10 +1972,11 @@ static void feed_byte(struct model *m, unsigned char c)
/* ---- public ---- */
-struct model *model_new(struct term_vector2i size, int history)
+struct model *model_new(struct term_vector2i size, int history, const char *name)
{
struct model *m = calloc(1, sizeof(*m));
if (!m) abort();
+ snprintf(m->name, sizeof(m->name), "%s", name && *name ? name : "term");
m->cols = size.x < 1 ? 80 : size.x;
m->rows = size.y < 1 ? 24 : size.y;
m->limit = history_limit(history);
diff --git a/tests/model.h b/tests/model.h
index 9a4316f..1707ef7 100644
--- a/tests/model.h
+++ b/tests/model.h
@@ -28,8 +28,9 @@ struct model_cell {
struct model;
-/* history is the history limit, as in term_options.scrollback. */
-struct model *model_new(struct term_vector2i size, int history);
+/* history is the history limit, as in term_options.scrollback. name is
+ * as in term_options.name. */
+struct model *model_new(struct term_vector2i size, int history, const char *name);
void model_free(struct model *m);
void model_feed(struct model *m, const char *s, size_t len, unsigned flags);
bool model_resize(struct model *m, struct term_vector2i size);
diff --git a/tests/model_row_test.c b/tests/model_row_test.c
index 7afca05..ad4ac2c 100644
--- a/tests/model_row_test.c
+++ b/tests/model_row_test.c
@@ -5,7 +5,7 @@
int main(void)
{
- struct model *m = model_new((struct term_vector2i){ .x = 10, .y = 3 }, 10);
+ struct model *m = model_new((struct term_vector2i){ .x = 10, .y = 3 }, 10, NULL);
model_feed(m, "A\r\nB\r\nC\r\nD", strlen("A\r\nB\r\nC\r\nD"), 0);
assert(model_history(m) == 1);
assert(model_row(m, -1) == NULL);
diff --git a/tests/model_test.c b/tests/model_test.c
index 395097e..e94307c 100644
--- a/tests/model_test.c
+++ b/tests/model_test.c
@@ -256,7 +256,7 @@ static int run(const struct script *s, enum chunking chunks, uint64_t chunk_seed
.callbacks = { .ud = &got, .reply = on_reply },
};
struct term *t = term_new(&opt);
- struct model *m = model_new(s->size, s->history);
+ struct model *m = model_new(s->size, s->history, NULL);
struct term_snapshot *snap = term_snapshot_new();
if (!t || !snap) abort();
uint64_t saved_rng = rng;
diff --git a/tests/osc_test.c b/tests/osc_test.c
index 41a9247..9d08157 100644
--- a/tests/osc_test.c
+++ b/tests/osc_test.c
@@ -104,6 +104,35 @@ static void reset_captures(void)
notify_count = 0;
}
+/* The notification default is term_options.name. A DCS +p does not change it. */
+static void test_host_name_notify(void)
+{
+ struct term_options opt = {
+ .size = { .x = 80, .y = 24 },
+ .scrollback = TEST_SCROLLBACK,
+ .name = "myterm",
+ .callbacks = { .notify = capture_notify },
+ };
+ struct term *t = term_new(&opt);
+ assert(t);
+ reset_captures();
+ write_seq(t, "\033]9;hello\007");
+ assert(notify_count == 1);
+ assert(!strcmp(notify_title, "myterm"));
+ assert(!strcmp(notify_body, "hello"));
+
+ write_seq(t, "\033P+p6f74686572\033\\");
+ reset_captures();
+ write_seq(t, "\033]9;again\007");
+ assert(notify_count == 1);
+ assert(!strcmp(notify_title, "myterm"));
+
+ write_seq(t, "\033]777;notify;Title;Body\007");
+ assert(notify_count == 2);
+ assert(!strcmp(notify_title, "Title"));
+ term_free(t);
+}
+
static void test_osc52(void)
{
struct term *t = osc_test_term();
@@ -289,7 +318,7 @@ static void test_osc_misc(void)
write_seq(t, "\033]9;hello there\033\\");
assert(notify_count == 1);
- assert(!strcmp(notify_title, "wsap_term"));
+ assert(!strcmp(notify_title, "term"));
assert(!strcmp(notify_body, "hello there"));
write_seq(t, "\033]9;4;3;50\007");
assert(notify_count == 1);
@@ -450,6 +479,7 @@ int main(void)
test_osc8();
test_sgr_reset_keeps_link();
test_osc_misc();
+ test_host_name_notify();
test_osc_colors();
test_osc_legacy_titles();
test_palette_and_theme();
diff --git a/tests/term_test.c b/tests/term_test.c
index 66dbd96..e4ffb27 100644
--- a/tests/term_test.c
+++ b/tests/term_test.c
@@ -1485,6 +1485,32 @@ static void test_string_payloads(void)
}
}
+/* term_new() refuses a name that a DCS reply could echo unsafely. */
+static void test_host_name_rejected(void)
+{
+ static const char *const bad[] = {
+ "\033x", "a\tb", "\177", "\300", "a\nb",
+ };
+ char longest[65];
+ memset(longest, 'a', 64);
+ longest[64] = 0;
+ char fits[64];
+ memset(fits, 'b', 63);
+ fits[63] = 0;
+
+ for (size_t i = 0; i < sizeof(bad) / sizeof(bad[0]); i++) {
+ struct term_options opt = { .size = { .x = 10, .y = 3 }, .name = bad[i] };
+ assert(!term_new(&opt));
+ }
+ struct term_options opt = { .size = { .x = 10, .y = 3 }, .name = longest };
+ assert(!term_new(&opt));
+
+ opt.name = fits;
+ struct term *t = term_new(&opt);
+ assert(t);
+ term_free(t);
+}
+
static void test_newline_policy_survives_reset(void)
{
static const char *const resets[] = { "\033c", "\033[!p", "\033[20l" };
@@ -1860,6 +1886,7 @@ int main(void)
test_alt_erase_scrollback();
test_snapshot_follows_new_terminal();
test_bulk_rows_survive_edits();
+ test_host_name_rejected();
assert(!strcmp(UNICODE_DATA_VERSION, "18.0.0"));
assert(!strcmp(term_unicode_version(), UNICODE_DATA_VERSION));