diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/meson.build | 4 | ||||
| -rw-r--r-- | tests/term_test.c | 96 |
2 files changed, 86 insertions, 14 deletions
diff --git a/tests/meson.build b/tests/meson.build index 629f6d5..4fda075 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -77,7 +77,7 @@ term_amalgamation = custom_target( term_amalgamation_lib = static_library( 'term-amalgamation', term_amalgamation[0], - dependencies: [m_dep], + dependencies: [m_dep, thread_dep], override_options: ['c_std=c11', 'warning_level=3', 'werror=true'], build_by_default: false, ) @@ -90,7 +90,7 @@ foreach entry : term_tests c_args: test_c_args, include_directories: [term_inc, test_helpers_inc], link_with: term_amalgamation_lib, - dependencies: [m_dep] + entry[2], + dependencies: [m_dep, thread_dep] + entry[2], build_by_default: false), env: test_env, suite: ['unit', 'amalgamation'], timeout: 180) endif diff --git a/tests/term_test.c b/tests/term_test.c index 51544ef..62b827f 100644 --- a/tests/term_test.c +++ b/tests/term_test.c @@ -4,6 +4,7 @@ #include <assert.h> #include <limits.h> #include <pthread.h> +#include <sched.h> #include <stdatomic.h> #include <stdbool.h> #include <stdio.h> @@ -1837,21 +1838,22 @@ struct concurrent_worker_args { _Atomic bool done; }; +static const char concurrent_stream[] = + "The quick brown fox jumps over the lazy dog.\r\n" + "\033[31mRed text\033[0m and \033[32mgreen text\033[0m.\r\n" + "Some unicode: 漢字, ひらがな, and emojis: 😀🚀🎉\r\n" + "\033[2J\033[HCleared and reset\r\n" + "Line 1\r\nLine 2\r\nLine 3\r\nLine 4\r\nLine 5\r\n" + "\033]7;file:///tmp/dir\007\033]22;pointer\007" + "\033]8;;https://example.com\007link\033]8;;\007\r\n"; + static void *concurrent_feed_worker(void *arg) { struct concurrent_worker_args *w = arg; - static const char stream[] = - "The quick brown fox jumps over the lazy dog.\r\n" - "\033[31mRed text\033[0m and \033[32mgreen text\033[0m.\r\n" - "Some unicode: 漢字, ひらがな, and emojis: 😀🚀🎉\r\n" - "\033[2J\033[HCleared and reset\r\n" - "Line 1\r\nLine 2\r\nLine 3\r\nLine 4\r\nLine 5\r\n" - "\033]7;file:///tmp/dir\007\033]22;pointer\007" - "\033]8;;https://example.com\007link\033]8;;\007\r\n"; - size_t len = strlen(stream); + size_t len = strlen(concurrent_stream); for (int i = 0; i < 2000; i++) { - term_feed(w->t, stream, len, 0); + term_feed(w->t, concurrent_stream, len, 0); } atomic_store(&w->done, true); return NULL; @@ -1859,10 +1861,11 @@ static void *concurrent_feed_worker(void *arg) static void test_concurrent_feed_and_snapshot(void) { - struct term *t = term_new(&(struct term_options){ + struct term_options opt = { .size = { .x = 80, .y = 24 }, .scrollback = 1000, - }); + }; + struct term *t = term_new(&opt); assert(t != NULL); struct concurrent_worker_args w = { .t = t, .done = false }; @@ -1903,6 +1906,33 @@ static void test_concurrent_feed_and_snapshot(void) bool ok = term_snapshot_update(snap, t); assert(ok); + /* The reader changed only the view and the selection. The screen and + * the history must match a terminal that got the same bytes on one + * thread. */ + struct term *ref = term_new(&opt); + assert(ref != NULL); + for (int i = 0; i < 2000; i++) + tt_write(ref, concurrent_stream, strlen(concurrent_stream)); + assert(tt_cols(t) == tt_cols(ref) && tt_rows(t) == tt_rows(ref)); + assert(tt_history(t) == tt_history(ref)); + assert(tt_cursor_x(t) == tt_cursor_x(ref) && tt_cursor_y(t) == tt_cursor_y(ref)); + char *got = malloc((size_t)tt_cols(t) + 1); + char *want = malloc((size_t)tt_cols(ref) + 1); + assert(got && want); + for (int y = 0; y < tt_rows(t); y++) { + row_text(t, y, got); + row_text(ref, y, want); + assert(!strcmp(got, want)); + } + free(got); + free(want); + char *text = term_history_text(t, 1000); + char *text_ref = term_history_text(ref, 1000); + assert(text && text_ref && !strcmp(text, text_ref)); + term_text_free(text); + term_text_free(text_ref); + + term_free(ref); term_snapshot_free(snap); term_free(t); } @@ -1928,10 +1958,52 @@ static void test_reserved_options_must_be_zero(void) term_free(t); } +struct lock_probe { + struct term *t; + _Atomic bool entered; +}; + +static void *lock_probe_worker(void *arg) +{ + struct lock_probe *p = arg; + term_lock(p->t); + atomic_store(&p->entered, true); + term_unlock(p->t); + return NULL; +} + +/* term_lock() nests on the owning thread. Another thread waits until the + * last term_unlock(). */ +static void test_lock_reentrant_across_threads(void) +{ + struct term *t = term_new(&(struct term_options){ + .size = { .x = 80, .y = 24 }, + }); + assert(t != NULL); + + term_lock(t); + term_lock(t); + assert(tt_cols(t) == 80); + term_unlock(t); + + struct lock_probe p = { .t = t, .entered = false }; + pthread_t th; + assert(pthread_create(&th, NULL, lock_probe_worker, &p) == 0); + for (int i = 0; i < 1000; i++) + sched_yield(); + assert(!atomic_load(&p.entered)); + term_unlock(t); + + assert(pthread_join(th, NULL) == 0); + assert(atomic_load(&p.entered)); + term_free(t); +} + int main(void) { test_reserved_options_must_be_zero(); test_concurrent_feed_and_snapshot(); + test_lock_reentrant_across_threads(); test_grow_past_reservation(); test_skipped_cells_read_blank(); test_csi_text_chunks(); |
