summaryrefslogtreecommitdiff
path: root/tests/snapshot_failure_test.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-15 12:42:11 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-15 12:42:11 +0300
commit6d2dc774947787dcef73a3251d1e9ec51bc526df (patch)
tree05f3c67039a9c6e69854e7de88ef9d40c110ec21 /tests/snapshot_failure_test.c
parent7b31d6a05357953c035e2ab05ec117ef748f7798 (diff)
Report selection allocation failure instead of truncating
On a screen taller than 256 rows the selection's row list is allocated. When that failed, it fell back to the 256-entry stack array, so the selection and the snapshot's selection spans silently stopped at row 256 while every call still reported success. lines_init() now reports the failure. term_select_start() and term_select_extend() return false and leave the selection unchanged, term_snapshot_update() fails and keeps its previous content, and the text getters return NULL, as they do for their other allocations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/snapshot_failure_test.c')
-rw-r--r--tests/snapshot_failure_test.c57
1 files changed, 52 insertions, 5 deletions
diff --git a/tests/snapshot_failure_test.c b/tests/snapshot_failure_test.c
index 6ce8f7b..7d4b139 100644
--- a/tests/snapshot_failure_test.c
+++ b/tests/snapshot_failure_test.c
@@ -327,9 +327,8 @@ static bool resize_at(int k)
return injected;
}
-/* More than 256 rows moves the scratch keys and spans off the stack. The
- * selection is left out: its own row list falls back to 256 rows when its
- * allocation fails, which is outside the snapshot. */
+/* More than 256 rows moves the scratch keys and spans, and the selection's
+ * row list, off the stack. */
static bool tall_screen_at(int k)
{
struct term_options options = { .size = { 8, 300 }, .scrollback = 5 };
@@ -339,6 +338,9 @@ static bool tall_screen_at(int k)
for (int y = 0; y < 299; y++)
term_feed(t, "\r\n", 2, 0);
term_feed(t, "e\xcc\x81yes", 5, 0);
+ assert(term_select_start(t, (struct term_vector2i){ .x = 0, .y = 299 }, false,
+ TERM_SELECT_CELL));
+ assert(term_select_extend(t, (struct term_vector2i){ .x = 3, .y = 299 }, true));
bool injected = update_failing_at(s, t, k);
@@ -346,6 +348,8 @@ static bool tall_screen_at(int k)
const struct term_cell *row = term_snapshot_row(s, 299);
uint32_t cps[4];
assert(cluster_cps(s, &row[0], cps, 4) == 2 && cps[0] == 'e' && cps[1] == 0x301);
+ struct term_span span = term_snapshot_row_selection(s, 299);
+ assert(span.begin == 0 && span.end == 4);
expect_matches_fresh(t, s);
term_snapshot_free(s);
@@ -375,6 +379,47 @@ static void tall_selection_is_kept(void)
term_free(t);
}
+/* On a screen taller than 256 rows, selecting needs memory. When none is
+ * left, the call fails and the selection stays as it was. */
+static void tall_selection_fails_cleanly(void)
+{
+ struct term_options options = { .size = { 8, 300 }, .scrollback = 5 };
+ struct term *t = term_new(&options);
+ assert(t);
+ for (int y = 0; y < 299; y++)
+ term_feed(t, "\r\n", 2, 0);
+ term_feed(t, "yes", 3, 0);
+ assert(term_select_start(t, (struct term_vector2i){ .x = 0, .y = 299 }, false,
+ TERM_SELECT_CELL));
+ assert(term_select_extend(t, (struct term_vector2i){ .x = 1, .y = 299 }, true));
+ char *before = term_selection_text(t);
+ assert(before && !strcmp(before, "ye"));
+
+ alloc_was_failed = false;
+ alloc_countdown = 0;
+ assert(!term_select_extend(t, (struct term_vector2i){ .x = 2, .y = 299 }, true));
+ alloc_countdown = -1;
+ assert(alloc_was_failed);
+
+ alloc_was_failed = false;
+ alloc_countdown = 0;
+ assert(!term_select_start(t, (struct term_vector2i){ .x = 2, .y = 299 }, false,
+ TERM_SELECT_WORD));
+ alloc_countdown = -1;
+ assert(alloc_was_failed);
+
+ char *after = term_selection_text(t);
+ assert(after && !strcmp(after, before));
+ term_text_free(before);
+ term_text_free(after);
+
+ assert(term_select_extend(t, (struct term_vector2i){ .x = 2, .y = 299 }, true));
+ char *grown = term_selection_text(t);
+ assert(grown && !strcmp(grown, "yes"));
+ term_text_free(grown);
+ term_free(t);
+}
+
/* Scroll with clusters in most lines. Each step fails every allocation in
* turn, then retries, and must match a fresh capture. */
static void scrolling_fails_cleanly(void)
@@ -417,10 +462,12 @@ int main(void)
/* Four reserve() buffers and the arena, after the screen grows. */
failures = sweep(resize_at);
assert(failures >= 5);
- /* Four reserve() buffers, the scratch array, and the arena. */
+ /* Four reserve() buffers, the scratch array, the selection's row list,
+ * and the arena. */
failures = sweep(tall_screen_at);
- assert(failures >= 6);
+ assert(failures >= 7);
tall_selection_is_kept();
+ tall_selection_fails_cleanly();
scrolling_fails_cleanly();
return 0;
}