summaryrefslogtreecommitdiff
path: root/tests/common
diff options
context:
space:
mode:
Diffstat (limited to 'tests/common')
-rw-r--r--tests/common/test_contracts.c10
-rw-r--r--tests/common/test_gl.c4
-rw-r--r--tests/common/test_loop.c204
-rw-r--r--tests/common/test_surface_integration.c110
-rw-r--r--tests/common/test_timers.c128
-rw-r--r--tests/common/test_vulkan.c2
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;