diff options
Diffstat (limited to 'tests/resize_failure_test.c')
| -rw-r--r-- | tests/resize_failure_test.c | 152 |
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 +} |
