summaryrefslogtreecommitdiff
path: root/tests/resize_failure_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/resize_failure_test.c')
-rw-r--r--tests/resize_failure_test.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/tests/resize_failure_test.c b/tests/resize_failure_test.c
new file mode 100644
index 0000000..5d7e63c
--- /dev/null
+++ b/tests/resize_failure_test.c
@@ -0,0 +1,152 @@
+#define _GNU_SOURCE
+#include "term_api.h"
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
+#define SANITIZED 1
+#elif __has_feature(address_sanitizer) || __has_feature(memory_sanitizer) || \
+ __has_feature(thread_sanitizer)
+#define SANITIZED 1
+#endif
+
+#ifndef SANITIZED
+static bool fail_next_calloc;
+static bool fail_reallocs;
+
+void *calloc(size_t n, size_t size)
+{
+ static void *(*real_calloc)(size_t, size_t);
+ if (!real_calloc)
+ real_calloc = (void *(*)(size_t, size_t))dlsym(RTLD_NEXT, "calloc");
+ if (fail_next_calloc) {
+ fail_next_calloc = false;
+ return NULL;
+ }
+ return real_calloc(n, size);
+}
+
+void *realloc(void *p, size_t size)
+{
+ static void *(*real_realloc)(void *, size_t);
+ if (!real_realloc)
+ real_realloc = (void *(*)(void *, size_t))dlsym(RTLD_NEXT, "realloc");
+ if (fail_reallocs)
+ return NULL;
+ return real_realloc(p, size);
+}
+#endif
+
+int main(void)
+{
+#ifdef SANITIZED
+ puts("resize failure test skipped under sanitizer");
+ return 0;
+#else
+ struct term *term = tt_new(10, 2);
+ assert(term);
+ tt_write(term, "abc", 3);
+
+ fail_next_calloc = true;
+ bool resized = tt_resize(term, 20, 2);
+ /* The resize must reach the failing allocation. */
+ assert(!fail_next_calloc);
+
+ /* A failed resize must leave the old terminal geometry usable as a unit. */
+ assert(!resized);
+ assert(tt_cols(term) == 10);
+ assert(tt_rows(term) == 2);
+ assert(tt_cell(term, 0, 0)->text == 'a');
+ assert(tt_cell(term, 2, 0)->text == 'c');
+
+ tt_write(term, "\r12345678901", 12);
+ assert(tt_cursor_x(term) == 1);
+ assert(tt_cursor_y(term) == 1);
+ assert(tt_cell(term, 0, 1)->text == '1');
+
+ /* The failed lazy allocation must not prevent a later resize retry. */
+ resized = tt_resize(term, 20, 2);
+ assert(resized);
+ assert(tt_cols(term) == 20);
+ assert(tt_rows(term) == 2);
+ assert(tt_cell(term, 0, 0)->text == '1');
+ term_free(term);
+
+ term = tt_new(10, 3);
+ assert(term);
+ const char margins[] = "\033[?69h\033[1;5s\033[H";
+ tt_write(term, margins, sizeof(margins) - 1);
+ fail_next_calloc = true;
+ resized = tt_resize(term, 20, 3);
+ assert(!fail_next_calloc);
+
+ /* A failed resize keeps the margins that fit the old width. */
+ assert(!resized);
+ assert(tt_cols(term) == 10);
+ tt_write(term, "abcdefg", 7);
+ assert(tt_cell(term, 4, 0)->text == 'e');
+ assert(tt_cell(term, 5, 0)->text != 'f');
+ assert(tt_cell(term, 0, 1)->text == 'f');
+ term_free(term);
+
+ term = tt_new(10, 2);
+ assert(term);
+ tt_write(term, "normal", 6);
+ fail_next_calloc = true;
+ tt_write(term, "\033[?1049h", 8);
+ assert(!fail_next_calloc);
+
+ /* A failed alternate-screen allocation keeps the normal screen active. */
+ assert(!term_modes(term).alternate);
+ assert(tt_cell(term, 0, 0)->text == 'n');
+ assert(tt_cell(term, 5, 0)->text == 'l');
+ tt_write(term, "\033[?1049l", 8);
+ assert(tt_cell(term, 0, 0)->text == 'n');
+ term_free(term);
+
+ /* A reflow that cannot grow its row buffers fails before it changes the
+ * history, so the old geometry and text survive the failed resize. The
+ * first resize sizes the buffers, so only the reflow's growth fails. */
+ term = tt_new(20, 40);
+ assert(term);
+ for (int i = 0; i < 40; i++) {
+ char line[32];
+ int n = snprintf(line, sizeof line, "row %02d 0123456789\r\n", i);
+ tt_write(term, line, (size_t)n);
+ }
+ resized = tt_resize(term, 21, 40);
+ assert(resized);
+ int history = tt_history(term);
+ char *before = tt_history_text(term, 1000);
+ assert(before);
+
+ fail_reallocs = true;
+ resized = tt_resize(term, 10, 40);
+ fail_reallocs = false;
+ assert(!resized);
+ assert(tt_cols(term) == 21);
+ assert(tt_rows(term) == 40);
+ assert(tt_history(term) == history);
+ char *after = tt_history_text(term, 1000);
+ assert(after && !strcmp(before, after));
+ free(before);
+ free(after);
+
+ resized = tt_resize(term, 10, 40);
+ assert(resized);
+ assert(tt_cols(term) == 10);
+ term_free(term);
+
+ puts("lazy screen allocation failures preserve observable terminal state");
+ return 0;
+#endif
+}