diff options
Diffstat (limited to 'tests/common')
| -rw-r--r-- | tests/common/test_contracts.c | 10 | ||||
| -rw-r--r-- | tests/common/test_gl.c | 4 | ||||
| -rw-r--r-- | tests/common/test_loop.c | 204 | ||||
| -rw-r--r-- | tests/common/test_surface_integration.c | 110 | ||||
| -rw-r--r-- | tests/common/test_timers.c | 128 | ||||
| -rw-r--r-- | tests/common/test_vulkan.c | 2 |
6 files changed, 365 insertions, 93 deletions
diff --git a/tests/common/test_contracts.c b/tests/common/test_contracts.c index f9f427f..004cd64 100644 --- a/tests/common/test_contracts.c +++ b/tests/common/test_contracts.c @@ -347,9 +347,6 @@ static bool popup_reposition(struct yasl_surface *popup, static uint32_t popup_caps(const struct yasl_private_display *d) { (void)d; return YASL_PRIVATE_CAP_POPUPS; } -static int dispatch_failure(struct yasl_private_display *d, int timeout) -{ (void)d; (void)timeout; return -1; } - static struct yasl_surface *create_failure(struct yasl_private_display *d, const struct yasl_private_window_spec *spec, void *device) { (void)d; (void)spec; (void)device; return NULL; } @@ -372,8 +369,7 @@ static void test_errors(void) .surface_create_toplevel = create_failure, .surface_create_popup = popup_create, .surface_popup_reposition = popup_reposition, - .display_drag_start = drag_without_listener, - .display_dispatch_timeout = dispatch_failure}; + .display_drag_start = drag_without_listener}; struct yasl_private_display d = {.ops = &ops}; struct yasl_surface s = {.display = &d}; struct yasl_private_popup_spec spec = {.size = {120, 80}, .anchor = {0, 0, 1, 1}}; @@ -399,11 +395,7 @@ static void test_errors(void) d.ops = &unsupported; assert(!yasl_private_surface_create_popup(&s, &spec) && errno == ENOTSUP); assert(!yasl_private_surface_popup_reposition(&s, &spec) && errno == ENOTSUP); - assert(yasl_private_display_dispatch(&d, 0) == -1 && errno == ENOTSUP); - assert(yasl_private_display_dispatch(NULL, 0) == -1 && errno == EINVAL); d.ops = &ops; - errno = ERANGE; - assert(yasl_private_display_dispatch(&d, 0) == -1 && errno == EIO); struct yasl_private_window_spec toplevel = {.size = {120, 80}}; errno = ERANGE; assert(!yasl_private_surface_create_toplevel(&d, &toplevel) && errno == ENODEV); diff --git a/tests/common/test_gl.c b/tests/common/test_gl.c index 2bf3d8c..1bb5bec 100644 --- a/tests/common/test_gl.c +++ b/tests/common/test_gl.c @@ -77,8 +77,8 @@ static void wait_for_size(yasl_surface *surface, int width, int height) int main(int argc, char **argv) { REQUIRE(argc == 2, "expected backend argument: wayland or x11"); - if (!strcmp(argv[1], "wayland")) REQUIRE(yasl_init(YASL_BACKEND_WAYLAND, NULL) == 0, "select Wayland backend"); - else if (!strcmp(argv[1], "x11")) REQUIRE(yasl_init(YASL_BACKEND_X11, NULL) == 0, "select X11 backend"); + if (!strcmp(argv[1], "wayland")) REQUIRE(yasl_init(&(struct yasl_options){.backend = YASL_BACKEND_WAYLAND}) == 0, "select Wayland backend"); + else if (!strcmp(argv[1], "x11")) REQUIRE(yasl_init(&(struct yasl_options){.backend = YASL_BACKEND_X11}) == 0, "select X11 backend"); else REQUIRE(false, "backend must be wayland or x11"); primary = yasl_surface_create(&(struct yasl_surface_options){.title="YASL GL test", .width=320, .height=240, .type=YASL_SURFACE_GL}); diff --git a/tests/common/test_loop.c b/tests/common/test_loop.c index 5c49691..bdc33b7 100644 --- a/tests/common/test_loop.c +++ b/tests/common/test_loop.c @@ -4,7 +4,9 @@ #include <assert.h> #include <errno.h> +#include <poll.h> #include <pthread.h> +#include <stdint.h> #include <signal.h> #include <stdio.h> #include <string.h> @@ -29,7 +31,7 @@ static void on_alarm(int signo) } struct post_log { - struct yasl_private_loop *loop; + struct yasl_loop *loop; char order[8]; int count; }; @@ -57,7 +59,7 @@ static void post_b(void *ud) static void test_post_order(void) { - struct yasl_private_loop *l = yasl_private_loop_create(); + struct yasl_loop *l = yasl_loop_create(); assert(l); assert(!yasl_private_loop_post(NULL, post_b, NULL)); assert(!yasl_private_loop_post(l, NULL, NULL)); @@ -65,20 +67,20 @@ static void test_post_order(void) struct post_log log = {.loop = l}; assert(yasl_private_loop_post(l, post_a, &log)); assert(yasl_private_loop_post(l, post_b, &log)); - assert(yasl_private_loop_step(l, 0) >= 1); + assert(yasl_loop_step(l, 0) >= 1); /* A post made while posts run waits for a later step. */ assert(log.count == 2 && memcmp(log.order, "ab", 2) == 0); - assert(yasl_private_loop_step(l, 0) >= 1); + assert(yasl_loop_step(l, 0) >= 1); assert(log.count == 3 && log.order[2] == 'c'); /* Destroy drops posts that never ran. */ assert(yasl_private_loop_post(l, post_b, &log)); - yasl_private_loop_destroy(l); + yasl_loop_destroy(l); assert(log.count == 3); } struct thread_ctx { - struct yasl_private_loop *loop; + struct yasl_loop *loop; bool kick; int signo; _Atomic bool ran; @@ -105,27 +107,27 @@ static void *wake_thread(void *ud) static void test_cross_thread(void) { - struct yasl_private_loop *l = yasl_private_loop_create(); + struct yasl_loop *l = yasl_loop_create(); assert(l); struct thread_ctx post = {.loop = l}; pthread_t t; assert(pthread_create(&t, NULL, wake_thread, &post) == 0); while (!post.ran) - assert(yasl_private_loop_step(l, -1) >= 0); + assert(yasl_loop_step(l, -1) >= 0); pthread_join(t, NULL); /* A kick ends an indefinite wait without running anything. */ struct thread_ctx kick = {.loop = l, .kick = true}; assert(pthread_create(&t, NULL, wake_thread, &kick) == 0); - assert(yasl_private_loop_step(l, -1) >= 1); + assert(yasl_loop_step(l, -1) >= 1); pthread_join(t, NULL); - yasl_private_loop_destroy(l); + yasl_loop_destroy(l); } struct signal_log { - struct yasl_private_loop *loop; + struct yasl_loop *loop; int seen[NSIG]; int total; bool unwatch; @@ -138,7 +140,7 @@ static void on_signal(void *ud, int signo) log->seen[signo]++; log->total++; if (log->unwatch) - yasl_private_loop_unwatch_signals(log->loop); + yasl_loop_unwatch_signals(log->loop); } static volatile sig_atomic_t previous_handler_hits; @@ -149,10 +151,10 @@ static void previous_handler(int signo) previous_handler_hits++; } -static void step_until(struct yasl_private_loop *l, const int *counter, int want) +static void step_until(struct yasl_loop *l, const int *counter, int want) { while (*counter < want) - assert(yasl_private_loop_step(l, -1) >= 0); + assert(yasl_loop_step(l, -1) >= 0); } static void test_signals(void) @@ -161,30 +163,26 @@ static void test_signals(void) sigemptyset(&prev.sa_mask); assert(sigaction(SIGUSR2, &prev, NULL) == 0); - struct yasl_private_loop *l = yasl_private_loop_create(); - struct yasl_private_loop *other = yasl_private_loop_create(); + struct yasl_loop *l = yasl_loop_create(); + struct yasl_loop *other = yasl_loop_create(); assert(l && other); struct signal_log log = {.loop = l}; const int bad[] = {SIGKILL}; - errno = 0; - assert(!yasl_private_loop_watch_signals(l, bad, 1, on_signal, &log) && errno == EINVAL); + assert(yasl_loop_watch_signals(l, bad, 1, on_signal, &log) == -EINVAL); const int sigs[] = {SIGUSR1, SIGUSR2, SIGCHLD}; - errno = 0; - assert(!yasl_private_loop_watch_signals(l, sigs, 0, on_signal, &log) && errno == EINVAL); - errno = 0; - assert(!yasl_private_loop_watch_signals(l, sigs, 3, NULL, &log) && errno == EINVAL); - assert(yasl_private_loop_watch_signals(l, sigs, 3, on_signal, &log)); + assert(yasl_loop_watch_signals(l, sigs, 0, on_signal, &log) == -EINVAL); + assert(yasl_loop_watch_signals(l, sigs, 3, NULL, &log) == -EINVAL); + assert(yasl_loop_watch_signals(l, sigs, 3, on_signal, &log) == 0); /* One signal belongs to one loop at a time. */ - errno = 0; - assert(!yasl_private_loop_watch_signals(other, sigs, 1, on_signal, &log) && errno == EBUSY); + assert(yasl_loop_watch_signals(other, sigs, 1, on_signal, &log) == -EBUSY); /* Deliveries before the callback runs coalesce per signal. */ raise(SIGUSR1); raise(SIGUSR1); raise(SIGUSR2); - assert(yasl_private_loop_step(l, 0) >= 1); + assert(yasl_loop_step(l, 0) >= 1); assert(log.seen[SIGUSR1] == 1 && log.seen[SIGUSR2] == 1); assert(previous_handler_hits == 0); @@ -203,13 +201,16 @@ static void test_signals(void) int status; assert(waitpid(child, &status, 0) == child); - /* Replacing the watch restores the signals it drops. */ + /* A loop has one watch, so a second cannot silently drop the first one's + * signals. Unwatching restores the previous actions. */ const int only_usr1[] = {SIGUSR1}; - assert(yasl_private_loop_watch_signals(l, only_usr1, 1, on_signal, &log)); + assert(yasl_loop_watch_signals(l, only_usr1, 1, on_signal, &log) == -EBUSY); + yasl_loop_unwatch_signals(l); + assert(yasl_loop_watch_signals(l, only_usr1, 1, on_signal, &log) == 0); raise(SIGUSR2); assert(previous_handler_hits == 1); - assert(yasl_private_loop_watch_signals(other, (const int[]){SIGUSR2}, 1, on_signal, &log)); - yasl_private_loop_unwatch_signals(other); + assert(yasl_loop_watch_signals(other, (const int[]){SIGUSR2}, 1, on_signal, &log) == 0); + yasl_loop_unwatch_signals(other); assert(previous_handler_hits == 1); raise(SIGUSR2); assert(previous_handler_hits == 2); @@ -221,21 +222,153 @@ static void test_signals(void) struct sigaction now; assert(sigaction(SIGUSR1, NULL, &now) == 0); assert(now.sa_handler == SIG_DFL); - yasl_private_loop_unwatch_signals(l); + yasl_loop_unwatch_signals(l); /* Destroy unwatches. */ log.unwatch = false; - assert(yasl_private_loop_watch_signals(l, only_usr1, 1, on_signal, &log)); - yasl_private_loop_destroy(l); + assert(yasl_loop_watch_signals(l, only_usr1, 1, on_signal, &log) == 0); + assert(yasl_loop_destroy(l) == 0); assert(sigaction(SIGUSR1, NULL, &now) == 0); assert(now.sa_handler == SIG_DFL); - yasl_private_loop_destroy(other); + yasl_loop_destroy(other); struct sigaction dfl = {.sa_handler = SIG_DFL}; sigemptyset(&dfl.sa_mask); sigaction(SIGUSR2, &dfl, NULL); } +struct fd_log { + int calls; + short revents; +}; + +static void on_readable(void *ud, int fd, short revents) +{ + struct fd_log *log = ud; + char b; + if (revents & POLLIN) + assert(read(fd, &b, 1) == 1); + log->calls++; + log->revents = revents; +} + +static void test_fds(void) +{ + yasl_loop *l = yasl_loop_create(); + assert(l); + int p[2]; + assert(pipe(p) == 0); + struct fd_log log = {0}; + assert(yasl_loop_add_fd(l, -1, POLLIN, on_readable, &log) == -EINVAL); + assert(yasl_loop_add_fd(l, p[0], POLLIN, NULL, &log) == -EINVAL); + int id = yasl_loop_add_fd(l, p[0], POLLIN, on_readable, &log); + assert(id > 0); + assert(yasl_loop_step(l, 0) == 0 && log.calls == 0); + assert(write(p[1], "x", 1) == 1); + assert(yasl_loop_step(l, -1) == 1 && log.calls == 1 && (log.revents & POLLIN)); + /* No requested events: a ready descriptor stays quiet. */ + assert(yasl_loop_set_fd_events(l, id, 0) == 0); + assert(write(p[1], "x", 1) == 1); + assert(yasl_loop_step(l, 0) == 0 && log.calls == 1); + assert(yasl_loop_set_fd_events(l, id, POLLIN) == 0); + assert(yasl_loop_step(l, 0) == 1 && log.calls == 2); + assert(yasl_loop_del_fd(l, id) == 0); + assert(yasl_loop_del_fd(l, id) == -ENOENT); + assert(yasl_loop_set_fd_events(l, id, POLLIN) == -ENOENT); + assert(write(p[1], "x", 1) == 1); + assert(yasl_loop_step(l, 0) == 0 && log.calls == 2); + close(p[0]); + close(p[1]); + assert(yasl_loop_destroy(l) == 0); +} + +struct reentry { + yasl_loop *loop; + int step, run; + bool quit; +}; + +static uint64_t reenter(void *ud, uint64_t id, uint64_t interval_ns) +{ + (void)id; (void)interval_ns; + struct reentry *r = ud; + r->step = yasl_loop_step(r->loop, 0); + r->run = yasl_loop_run(r->loop); + if (r->quit) + yasl_loop_quit(r->loop); + return 0; +} + +static void *step_elsewhere(void *ud) +{ + return (void *)(intptr_t)yasl_loop_step(ud, 0); +} + +static void test_reentry_and_quit(void) +{ + yasl_loop *l = yasl_loop_create(); + assert(l); + /* A callback cannot step its own loop; the nested run keeps the outer quit. */ + struct reentry r = {.loop = l, .quit = true}; + assert(yasl_loop_add_timer(l, 0, reenter, &r)); + assert(yasl_loop_run(l) == 0); + assert(r.step == -EBUSY && r.run == -EBUSY); + + /* A quit before the run ends it at once, and the run clears it. */ + yasl_loop_quit(l); + assert(yasl_loop_run(l) == 0); + r = (struct reentry){.loop = l, .quit = true}; + assert(yasl_loop_add_timer(l, YASL_PRIVATE_MS_TO_NS(5), reenter, &r)); + assert(yasl_loop_run(l) == 0 && r.step == -EBUSY); + + /* Only the creating thread may step. */ + pthread_t t; + void *result; + assert(pthread_create(&t, NULL, step_elsewhere, l) == 0); + pthread_join(t, &result); + assert((intptr_t)result == -EPERM); + assert(yasl_loop_destroy(l) == 0); +} + +static void on_interrupt(int signo) +{ + (void)signo; +} + +struct interrupter { + pthread_t target; + int delay_ms; +}; + +static void *interrupt_thread(void *ud) +{ + struct interrupter *i = ud; + sleep_ms(i->delay_ms); + pthread_kill(i->target, SIGUSR2); + return NULL; +} + +/* An interrupted wait resumes with the time left, not the whole timeout. */ +static void test_interrupted_wait(void) +{ + struct sigaction sa = {.sa_handler = on_interrupt}; + sigemptyset(&sa.sa_mask); + struct sigaction old; + assert(sigaction(SIGUSR2, &sa, &old) == 0); + yasl_loop *l = yasl_loop_create(); + assert(l); + struct interrupter i = {.target = pthread_self(), .delay_ms = 60}; + pthread_t t; + uint64_t start = yasl_time_ns(); + assert(pthread_create(&t, NULL, interrupt_thread, &i) == 0); + assert(yasl_loop_step(l, 100) == 0); + uint64_t elapsed_ms = (yasl_time_ns() - start) / 1000000u; + pthread_join(t, NULL); + assert(elapsed_ms >= 90 && elapsed_ms < 140); + assert(yasl_loop_destroy(l) == 0); + sigaction(SIGUSR2, &old, NULL); +} + int main(void) { signal(SIGALRM, on_alarm); @@ -243,6 +376,9 @@ int main(void) test_post_order(); test_cross_thread(); test_signals(); + test_fds(); + test_reentry_and_quit(); + test_interrupted_wait(); printf("Loop tests passed.\n"); return 0; } diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c index f33e35b..7862bfc 100644 --- a/tests/common/test_surface_integration.c +++ b/tests/common/test_surface_integration.c @@ -34,6 +34,102 @@ static void *wrong_thread(void *data) assert(yasl_surface_get_native(data, &native) == -EPERM && !native.wl_display && !native.x11_display); return NULL; } +struct callback_state { + yasl_loop *loop; + yasl_surface *window; + int refreshes; + int wait_result, shutdown_result; + yasl_surface *made; + int made_destroy; +}; +static void on_event(void *userdata, const yasl_event *e) +{ + struct callback_state *st = userdata; + if (e->type == YASL_EVENT_REFRESH && e->surface == st->window) { + st->refreshes++; + int w = 0, h = 0; + uint32_t *pixels = yasl_cpu_pixels(e->surface, &w, &h); + assert(pixels && w > 0 && h > 0); + pixels[0] = 0xff336699u; + assert(yasl_cpu_present(e->surface) == 0); + } + yasl_event inner; + st->wait_result = yasl_wait_event(&inner, 0); + st->shutdown_result = yasl_shutdown(); +} +/* A loop callback runs with the display idle, so surface creation and its + * roundtrips are allowed there. */ +static uint64_t create_in_timer(void *userdata, uint64_t id, uint64_t delay_ns) +{ + (void)id; (void)delay_ns; + struct callback_state *st = userdata; + st->made = yasl_surface_create(&(struct yasl_surface_options){ + .title = "made by a timer", .width = 64, .height = 64}); + assert(st->made); + st->made_destroy = yasl_surface_destroy(st->made); + return 0; +} +static uint64_t quit_in_timer(void *userdata, uint64_t id, uint64_t delay_ns) +{ + (void)id; (void)delay_ns; + yasl_loop_quit(userdata); + return 0; +} +static void step_until(struct callback_state *st, const int *counter, int want) +{ + uint64_t deadline = yasl_time_ns() + 5000000000ull; + while (*counter < want && yasl_time_ns() < deadline) + assert(yasl_loop_step(st->loop, 50) >= 0); + assert(*counter >= want); +} +static void test_callback_mode(enum yasl_backend backend) +{ + struct callback_state st = {.loop = yasl_loop_create()}; + assert(st.loop); + const struct yasl_options options = { + .backend = backend, .loop = st.loop, .on_event = on_event, .userdata = &st}; + assert(yasl_init(&options) == 0); + assert(yasl_init(&options) == -EALREADY); + assert(yasl_loop_destroy(st.loop) == -EBUSY); + yasl_event e; + assert(yasl_wait_event(&e, 0) == -EBUSY); + st.window = yasl_surface_create(&(struct yasl_surface_options){ + .title = "callback mode", .width = 96, .height = 64}); + assert(st.window); + + /* Events reach on_event while the application only steps its loop. */ + step_until(&st, &st.refreshes, 1); + assert(st.wait_result == -EBUSY && st.shutdown_result == -EBUSY); + + /* Frame requests coalesce into one REFRESH, sent once the window can + * show a frame. */ + int before = st.refreshes; + assert(yasl_surface_request_frame(st.window) == 0); + assert(yasl_surface_request_frame(st.window) == 0); + step_until(&st, &st.refreshes, before + 1); + for (int i = 0; i < 4; i++) + assert(yasl_loop_step(st.loop, 25) >= 0); + assert(st.refreshes == before + 1); + + /* An idle loop waits instead of spinning. */ + int steps = 0; + uint64_t end = yasl_time_ns() + 300000000ull; + while (yasl_time_ns() < end) { + assert(yasl_loop_step(st.loop, 100) >= 0); + steps++; + } + assert(steps < 30); + + assert(yasl_loop_add_timer(st.loop, 0, create_in_timer, &st)); + while (!st.made) + assert(yasl_loop_step(st.loop, 50) >= 0); + assert(st.made_destroy == 0); + + assert(yasl_loop_add_timer(st.loop, YASL_PRIVATE_MS_TO_NS(10), quit_in_timer, st.loop)); + assert(yasl_loop_run(st.loop) == 0); + assert(yasl_shutdown() == 0); + assert(yasl_loop_destroy(st.loop) == 0); +} static void push(yasl_surface *s, struct yasl_private_event event) { event.surface = s; @@ -68,8 +164,11 @@ int main(int argc, char **argv) enum yasl_backend backend = !strcmp(argv[1], "wayland") ? YASL_BACKEND_WAYLAND : !strcmp(argv[1], "x11") ? YASL_BACKEND_X11 : YASL_BACKEND_COCOA; - assert(yasl_init(backend, "org.yasl.surface-test") == 0); - assert(yasl_init(backend, NULL) == -EALREADY); + const struct yasl_options options = {.backend = backend, .app_id = "org.yasl.surface-test"}; + assert(yasl_init(&options) == 0); + assert(yasl_init(&options) == -EALREADY); + /* Other options would need another display. */ + assert(yasl_init(&(struct yasl_options){.backend = backend}) == -EBUSY); assert(yasl_get_backend() == backend); unsigned features = yasl_get_features(); yasl_surface *s = yasl_surface_create( @@ -329,16 +428,17 @@ int main(int argc, char **argv) assert(yasl_surface_destroy(s) == 0); if (popup) assert(!listed(display, popup_id)); - assert(yasl_init(backend, NULL) == -EALREADY); + assert(yasl_init(&options) == -EALREADY); s = yasl_surface_create(&(struct yasl_surface_options){.title = "shared backend remains", .width = 64, .height = 64}); assert(s); yasl_shutdown(); - assert(yasl_init(backend, NULL) == 0); + assert(yasl_init(&(struct yasl_options){.backend = backend}) == 0); s = yasl_surface_create( &(struct yasl_surface_options){.title = "reinitialized", .width = 64, .height = 64}); assert(s); yasl_shutdown(); - puts("public surface lifecycle, pixels, ownership, input, wake and refresh passed"); + test_callback_mode(backend); + puts("public surface lifecycle, pixels, ownership, input, wake, refresh and callback mode passed"); return 0; } diff --git a/tests/common/test_timers.c b/tests/common/test_timers.c index 2c3542f..3a35819 100644 --- a/tests/common/test_timers.c +++ b/tests/common/test_timers.c @@ -3,15 +3,17 @@ #include "common/types.h" #include <assert.h> +#include <signal.h> #include <stdio.h> #include <string.h> +#include <unistd.h> struct test_ctx { int count; uint64_t last_time; yasl_private_timer_id id; yasl_private_timer_id other_id; - struct yasl_private_loop *loop; + struct yasl_loop *loop; struct yasl_surface *surface; bool done; }; @@ -21,11 +23,32 @@ static uint64_t oneshot_cb(void *ud, yasl_private_timer_id id, uint64_t interval { struct test_ctx *ctx = ud; assert(id == ctx->id); - assert(interval_ns > 0); + (void)interval_ns; ctx->count++; return 0; /* Disarm */ } +static uint64_t count_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) +{ + (void)id; (void)interval_ns; + (*(int *)ud)++; + return 0; +} + +/* Adds enough timers to move the timer array, then repeats itself. */ +static uint64_t grow_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) +{ + struct test_ctx *ctx = ud; + assert(id == ctx->id); + if (ctx->count++ == 0) { + for (int i = 0; i < 64; i++) + assert(yasl_loop_add_timer(ctx->loop, YASL_PRIVATE_MS_TO_NS(60000), count_cb, NULL)); + return YASL_PRIVATE_MS_TO_NS(2); + } + assert(interval_ns == YASL_PRIVATE_MS_TO_NS(2)); + return 0; +} + /* 2. Repeating timer: ticks N times, then returns 0. */ static uint64_t repeat_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) { @@ -73,7 +96,7 @@ static uint64_t parent_timer_cb(void *ud, yasl_private_timer_id id, uint64_t int struct test_ctx *ctx = ud; assert(id == ctx->id); ctx->count += 1; - ctx->other_id = yasl_private_loop_add_timer(ctx->loop, 1000000ull, child_timer_cb, ctx); + ctx->other_id = yasl_loop_add_timer(ctx->loop, 1000000ull, child_timer_cb, ctx); assert(ctx->other_id > 0); return 0; } @@ -91,7 +114,7 @@ static uint64_t killer_timer_cb(void *ud, yasl_private_timer_id id, uint64_t int (void)id; (void)interval_ns; struct test_ctx *ctx = ud; ctx->count++; - assert(yasl_private_loop_del_timer(ctx->loop, ctx->other_id)); + assert(yasl_loop_del_timer(ctx->loop, ctx->other_id)); return 0; } @@ -111,90 +134,109 @@ static uint64_t ms_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interva static void test_loop_timers(void) { - struct yasl_private_loop *l = yasl_private_loop_create(); + struct yasl_loop *l = yasl_loop_create(); assert(l); /* Invalid arguments */ - assert(yasl_private_loop_add_timer(NULL, 1000, oneshot_cb, NULL) == 0); - assert(yasl_private_loop_add_timer(l, 0, oneshot_cb, NULL) == 0); - assert(yasl_private_loop_add_timer(l, 1000, NULL, NULL) == 0); - assert(!yasl_private_loop_del_timer(NULL, 1)); - assert(!yasl_private_loop_del_timer(l, 0)); - assert(!yasl_private_loop_del_timer(l, 99999)); + assert(yasl_loop_add_timer(NULL, 1000, oneshot_cb, NULL) == 0); + assert(yasl_loop_add_timer(l, 1000, NULL, NULL) == 0); + assert(!yasl_loop_del_timer(NULL, 1)); + assert(!yasl_loop_del_timer(l, 0)); + assert(!yasl_loop_del_timer(l, 99999)); assert(yasl_private_loop_next_timeout_ms(l) == -1); /* Test 1: Single-shot */ struct test_ctx ctx1 = { .loop = l }; - ctx1.id = yasl_private_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx1); + ctx1.id = yasl_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx1); assert(ctx1.id > 0); assert(yasl_private_loop_next_timeout_ms(l) >= 0); while (ctx1.count == 0) { - int handled = yasl_private_loop_step(l, 50); + int handled = yasl_loop_step(l, 50); assert(handled >= 0); } assert(ctx1.count == 1); /* Should be disarmed and removed */ - assert(!yasl_private_loop_del_timer(l, ctx1.id)); + assert(!yasl_loop_del_timer(l, ctx1.id)); assert(yasl_private_loop_next_timeout_ms(l) == -1); /* Test 2: Repeating */ struct test_ctx ctx2 = { .loop = l }; - ctx2.id = yasl_private_loop_add_timer(l, 2000000ull, repeat_cb, &ctx2); + ctx2.id = yasl_loop_add_timer(l, 2000000ull, repeat_cb, &ctx2); assert(ctx2.id > 0); while (!ctx2.done) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx2.count == 5); - assert(!yasl_private_loop_del_timer(l, ctx2.id)); + assert(!yasl_loop_del_timer(l, ctx2.id)); /* Test 3: Reschedule dynamically */ struct test_ctx ctx3 = { .loop = l }; - ctx3.id = yasl_private_loop_add_timer(l, 2000000ull, reschedule_cb, &ctx3); + ctx3.id = yasl_loop_add_timer(l, 2000000ull, reschedule_cb, &ctx3); assert(ctx3.id > 0); while (!ctx3.done) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx3.count == 2); /* Test 4: Timer cancellation before firing */ struct test_ctx ctx4 = { .loop = l }; - yasl_private_timer_id cancel_id = yasl_private_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx4); + yasl_private_timer_id cancel_id = yasl_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx4); assert(cancel_id > 0); - assert(yasl_private_loop_del_timer(l, cancel_id)); - assert(!yasl_private_loop_del_timer(l, cancel_id)); + assert(yasl_loop_del_timer(l, cancel_id)); + assert(!yasl_loop_del_timer(l, cancel_id)); /* Step for 10ms; timer should not fire */ - yasl_private_loop_step(l, 10); + yasl_loop_step(l, 10); assert(ctx4.count == 0); /* Test 5: Child timer added inside callback */ struct test_ctx ctx5 = { .loop = l }; - ctx5.id = yasl_private_loop_add_timer(l, 2000000ull, parent_timer_cb, &ctx5); + ctx5.id = yasl_loop_add_timer(l, 2000000ull, parent_timer_cb, &ctx5); while (ctx5.count < 11) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx5.count == 11); /* Test 6: Killer timer deletes victim timer before victim fires */ struct test_ctx ctx6 = { .loop = l }; - ctx6.id = yasl_private_loop_add_timer(l, 1000000ull, killer_timer_cb, &ctx6); - ctx6.other_id = yasl_private_loop_add_timer(l, 10000000ull, victim_timer_cb, &ctx6); + ctx6.id = yasl_loop_add_timer(l, 1000000ull, killer_timer_cb, &ctx6); + ctx6.other_id = yasl_loop_add_timer(l, 10000000ull, victim_timer_cb, &ctx6); while (ctx6.count == 0) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx6.count == 1); - yasl_private_loop_step(l, 20); + yasl_loop_step(l, 20); + + /* Test 8: Zero delay runs in the next step without waiting, and IDs are + * never handed out again. */ + int zero_runs = 0; + yasl_private_timer_id zero = yasl_loop_add_timer(l, 0, count_cb, &zero_runs); + assert(zero > 0 && yasl_private_loop_next_timeout_ms(l) == 0); + assert(yasl_loop_step(l, -1) == 1 && zero_runs == 1); + yasl_private_timer_id next = yasl_loop_add_timer(l, 0, count_cb, &zero_runs); + assert(next > zero); + assert(!yasl_loop_del_timer(l, zero) && yasl_loop_del_timer(l, next)); + + /* Test 9: A callback that grows the timer array still reschedules itself. */ + struct yasl_loop *grown = yasl_loop_create(); + assert(grown); + struct test_ctx ctx9 = { .loop = grown }; + ctx9.id = yasl_loop_add_timer(grown, 0, grow_cb, &ctx9); + while (ctx9.count < 2) + assert(yasl_loop_step(grown, 50) >= 0); + assert(!yasl_private_loop_timer_armed(grown, ctx9.id)); + assert(yasl_loop_destroy(grown) == 0); /* Test 7: Millisecond conversion */ struct test_ctx ctx7 = { .loop = l }; - ctx7.id = yasl_private_loop_add_timer(l, YASL_PRIVATE_MS_TO_NS(5), ms_timer_cb, &ctx7); + ctx7.id = yasl_loop_add_timer(l, YASL_PRIVATE_MS_TO_NS(5), ms_timer_cb, &ctx7); assert(ctx7.id > 0); while (!ctx7.done) { - yasl_private_loop_step(l, 50); + yasl_loop_step(l, 50); } assert(ctx7.count == 3); - yasl_private_loop_destroy(l); + yasl_loop_destroy(l); } /* Surface timer tests */ @@ -220,14 +262,14 @@ static uint64_t surface_suicide_cb(void *ud, yasl_private_timer_id id, uint64_t static void test_surface_timers(void) { - struct yasl_private_loop *loop = yasl_private_loop_create(); + struct yasl_loop *loop = yasl_loop_create(); assert(loop); struct yasl_private_display_spec spec = { .app_id = "org.yasl.timer_test" }; struct yasl_private_display *d = yasl_private_display_open(loop, &spec); if (!d) { /* Headless without display server */ - yasl_private_loop_destroy(loop); + yasl_loop_destroy(loop); return; } @@ -243,10 +285,10 @@ static void test_surface_timers(void) /* Application timers use the display's loop. */ struct test_ctx dctx = {0}; - dctx.id = yasl_private_loop_add_timer(yasl_private_display_loop(d), 2000000ull, oneshot_cb, &dctx); + dctx.id = yasl_loop_add_timer(yasl_private_display_loop(d), 2000000ull, oneshot_cb, &dctx); assert(dctx.id > 0); while (dctx.count == 0) { - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); } assert(dctx.count == 1); @@ -255,12 +297,12 @@ static void test_surface_timers(void) sctx.id = yasl_private_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx); assert(sctx.id > 0); while (sctx.count < 3) { - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); } assert(sctx.count >= 3); assert(yasl_private_surface_del_timer(s, sctx.id)); int count_before = sctx.count; - yasl_private_display_dispatch(d, 10); + yasl_loop_step(loop, 10); assert(sctx.count == count_before); /* A fired one-shot surface timer is retired, as a loop timer is. Many @@ -270,7 +312,7 @@ static void test_surface_timers(void) octx.id = yasl_private_surface_add_timer(s, 1000000ull, oneshot_cb, &octx); assert(octx.id > 0); while (octx.count == 0) - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); assert(!yasl_private_surface_del_timer(s, octx.id)); } @@ -280,7 +322,7 @@ static void test_surface_timers(void) assert(sctx2.id > 0); yasl_private_surface_destroy(s); /* Step display/loop; timer must not fire on dead surface */ - yasl_private_display_dispatch(d, 10); + yasl_loop_step(loop, 10); assert(sctx2.count == 0); /* Surface suicide from timer callback */ @@ -290,17 +332,19 @@ static void test_surface_timers(void) sctx3.id = yasl_private_surface_add_timer(s, 2000000ull, surface_suicide_cb, &sctx3); assert(sctx3.id > 0); while (sctx3.count == 0) { - yasl_private_display_dispatch(d, 50); + yasl_loop_step(loop, 50); } assert(sctx3.count == 1); assert(sctx3.surface == NULL); yasl_private_display_close(d); - yasl_private_loop_destroy(loop); + yasl_loop_destroy(loop); } int main(void) { + /* A wait that never ends fails here, not at the suite timeout. */ + alarm(10); test_loop_timers(); test_surface_timers(); printf("Timer tests passed successfully.\n"); diff --git a/tests/common/test_vulkan.c b/tests/common/test_vulkan.c index 4633d55..80f9cbe 100644 --- a/tests/common/test_vulkan.c +++ b/tests/common/test_vulkan.c @@ -41,7 +41,7 @@ static int run_test(enum yasl_backend backend) VkSemaphore acquired = VK_NULL_HANDLE, rendered = VK_NULL_HANDLE; VkCommandBuffer command = VK_NULL_HANDLE; - if (yasl_init(backend, NULL) < 0) { + if (yasl_init(&(struct yasl_options){.backend = backend}) < 0) { fprintf(stderr, "vulkan integration: cannot select requested backend %d (YASL error %d)\n", backend, errno); goto cleanup; |
