diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/common/test_gl.c | 19 | ||||
| -rw-r--r-- | tests/common/test_loop.c | 45 | ||||
| -rw-r--r-- | tests/common/test_surface_integration.c | 46 |
3 files changed, 108 insertions, 2 deletions
diff --git a/tests/common/test_gl.c b/tests/common/test_gl.c index 1bb5bec..0b91bff 100644 --- a/tests/common/test_gl.c +++ b/tests/common/test_gl.c @@ -146,6 +146,25 @@ int main(int argc, char **argv) clear(GL_COLOR_BUFFER_BIT_); REQUIRE((yasl_gl_present(primary) == 0), "swap resized GL surface"); + /* An animation that asks for each next frame gets one REFRESH per frame + * shown, even though GL surfaces also get a REFRESH when a frame is shown. */ + REQUIRE(yasl_surface_request_frame(primary) == 0, "request a frame"); + int frames = 0; + double animation_end = seconds_now() + 1.0; + while (seconds_now() < animation_end) { + yasl_event event = {0}; + int result = yasl_wait_event(&event, 50); + REQUIRE(result >= 0, "wait during animation"); + if (result == 0 || event.type != YASL_EVENT_REFRESH || event.surface != primary) + continue; + frames++; + REQUIRE(yasl_surface_request_frame(primary) == 0, "request the next frame"); + clear(GL_COLOR_BUFFER_BIT_); + REQUIRE(yasl_gl_present(primary) == 0, "present an animation frame"); + } + fprintf(stderr, "%d REFRESH events in 1 s of GL animation\n", frames); + REQUIRE(frames >= 20 && frames <= 75, "one REFRESH per frame shown"); + REQUIRE(yasl_surface_create(&(struct yasl_surface_options){.title="invalid attrs", .width=100, .height=100, .type=YASL_SURFACE_GL, .gl = {.major = -1}}) == NULL && errno == EINVAL, "reject invalid GL context options"); cpu = yasl_surface_create(&(struct yasl_surface_options){.title="CPU surface", .width=100, .height=100}); diff --git a/tests/common/test_loop.c b/tests/common/test_loop.c index bdc33b7..a77b6a5 100644 --- a/tests/common/test_loop.c +++ b/tests/common/test_loop.c @@ -237,6 +237,47 @@ static void test_signals(void) sigaction(SIGUSR2, &dfl, NULL); } +struct order_log { + char order[8]; + int count; +}; + +static void order_signal(void *ud, int signo) +{ + (void)signo; + struct order_log *log = ud; + log->order[log->count++] = 's'; +} + +static void order_fd(void *ud, int fd, short revents) +{ + (void)revents; + struct order_log *log = ud; + char b; + assert(read(fd, &b, 1) == 1); + log->order[log->count++] = 'f'; +} + +/* A signal runs after the descriptors of its step, so a child's last output + * is read before its exit is handled. */ +static void test_signal_order(void) +{ + yasl_loop *l = yasl_loop_create(); + assert(l); + struct order_log log = {0}; + assert(yasl_loop_watch_signals(l, (const int[]){SIGUSR1}, 1, order_signal, &log) == 0); + int p[2]; + assert(pipe(p) == 0); + assert(yasl_loop_add_fd(l, p[0], POLLIN, order_fd, &log) > 0); + assert(write(p[1], "x", 1) == 1); + raise(SIGUSR1); + assert(yasl_loop_step(l, 0) >= 1); + assert(log.count == 2 && memcmp(log.order, "fs", 2) == 0); + close(p[0]); + close(p[1]); + assert(yasl_loop_destroy(l) == 0); +} + struct fd_log { int calls; short revents; @@ -364,7 +405,8 @@ static void test_interrupted_wait(void) 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); + /* Lateness under load is not the bug; an early return is. */ + assert(elapsed_ms >= 90 && elapsed_ms < 1000); assert(yasl_loop_destroy(l) == 0); sigaction(SIGUSR2, &old, NULL); } @@ -376,6 +418,7 @@ int main(void) test_post_order(); test_cross_thread(); test_signals(); + test_signal_order(); test_fds(); test_reentry_and_quit(); test_interrupted_wait(); diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c index 7862bfc..6946cf9 100644 --- a/tests/common/test_surface_integration.c +++ b/tests/common/test_surface_integration.c @@ -37,7 +37,8 @@ static void *wrong_thread(void *data) struct callback_state { yasl_loop *loop; yasl_surface *window; - int refreshes; + int refreshes, resizes, refreshes_at_resize, final_losses; + bool animating; int wait_result, shutdown_result; yasl_surface *made; int made_destroy; @@ -45,8 +46,18 @@ struct callback_state { static void on_event(void *userdata, const yasl_event *e) { struct callback_state *st = userdata; + if (e->type == YASL_EVENT_DISPLAY_LOST && e->action == YASL_LOST_FINAL) + st->final_losses++; + if (e->type == YASL_EVENT_WINDOW && e->surface == st->window && + e->window.width == 120 && e->window.height == 80) { + st->resizes++; + st->refreshes_at_resize = st->refreshes; + } if (e->type == YASL_EVENT_REFRESH && e->surface == st->window) { st->refreshes++; + /* An animation asks for its next frame before it presents this one. */ + if (st->animating) + assert(yasl_surface_request_frame(e->surface) == 0); int w = 0, h = 0; uint32_t *pixels = yasl_cpu_pixels(e->surface, &w, &h); assert(pixels && w > 0 && h > 0); @@ -111,6 +122,32 @@ static void test_callback_mode(enum yasl_backend backend) assert(yasl_loop_step(st.loop, 25) >= 0); assert(st.refreshes == before + 1); + /* The frame an animation presents after its request is the frame the + * request waits for: one REFRESH per display refresh, not two. */ + st.animating = true; + assert(yasl_surface_request_frame(st.window) == 0); + before = st.refreshes; + uint64_t start = yasl_time_ns(); + while (yasl_time_ns() - start < 1000000000ull) + assert(yasl_loop_step(st.loop, 50) >= 0); + st.animating = false; + int frames = st.refreshes - before; + fprintf(stderr, "%d REFRESH events in 1 s of animation\n", frames); + assert(frames >= 20 && frames <= 75); + + /* A resize owes one REFRESH, after its WINDOW event. */ + for (int i = 0; i < 6; i++) + assert(yasl_loop_step(st.loop, 25) >= 0); + before = st.refreshes; + assert(yasl_surface_request_size(st.window, 120, 80) == 0); + step_until(&st, &st.resizes, 1); + for (int i = 0; i < 8; i++) + assert(yasl_loop_step(st.loop, 25) >= 0); + fprintf(stderr, "%d REFRESH events for one resize, %d before its WINDOW event\n", + st.refreshes - before, st.refreshes_at_resize - before); + assert(st.resizes == 1 && st.refreshes_at_resize == before); + assert(st.refreshes == before + 1); + /* An idle loop waits instead of spinning. */ int steps = 0; uint64_t end = yasl_time_ns() + 300000000ull; @@ -125,6 +162,13 @@ static void test_callback_mode(enum yasl_backend backend) assert(yasl_loop_step(st.loop, 50) >= 0); assert(st.made_destroy == 0); + /* A lost event ends the session: on_event hears it as a final loss. A + * string event with no text cannot be queued. */ + struct yasl_private_event broken = {.type = YASL_PRIVATE_EVENT_PASTE, .surface = st.window, + .text = {NULL, 1}}; + assert(!yasl_private_events_push(st.window->display, st.window, &broken)); + step_until(&st, &st.final_losses, 1); + 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); |
