summaryrefslogtreecommitdiff
path: root/tests/common
diff options
context:
space:
mode:
Diffstat (limited to 'tests/common')
-rw-r--r--tests/common/test_app.c72
-rw-r--r--tests/common/test_events.c209
-rw-r--r--tests/common/test_hello_example.c2
3 files changed, 269 insertions, 14 deletions
diff --git a/tests/common/test_app.c b/tests/common/test_app.c
index f61a957..214d8f2 100644
--- a/tests/common/test_app.c
+++ b/tests/common/test_app.c
@@ -2,6 +2,7 @@
#include "yasl/app.h"
#include <assert.h>
+#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
@@ -16,13 +17,16 @@ struct test_state {
bool quit_called;
int frame_count;
yasl_timer_id timer_id;
+ pthread_t worker;
+ uint64_t target_id;
};
static void *worker_thread(void *arg)
{
struct test_state *state = arg;
usleep(50000); /* 50ms */
- bool ok = yasl_post_event(state->display, state->surface, 42, (void *)0x1234, 0);
+ bool ok = yasl_display_post(state->display, &(struct yasl_message){
+ .target_id = state->target_id, .code = 42, .payload = (void *)0x1234});
assert(ok);
return NULL;
}
@@ -54,13 +58,13 @@ static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
yasl_surface_set_pointer_mode(state->surface, YASL_POINTER_RELATIVE);
yasl_surface_set_pointer_mode(state->surface, YASL_POINTER_NORMAL);
- state->timer_id = yasl_app_add_timer(d, YASL_MS_TO_NS(20), on_timer, state);
+ state->timer_id = yasl_loop_add_timer(yasl_display_loop(d), YASL_MS_TO_NS(20), on_timer, state);
assert(state->timer_id > 0);
- pthread_t thread;
- int err = pthread_create(&thread, NULL, worker_thread, state);
+ state->target_id = yasl_surface_id(state->surface);
+ int err = pthread_create(&state->worker, NULL, worker_thread, state);
assert(err == 0);
- pthread_detach(thread);
+
yasl_surface_request_frame(state->surface);
return YASL_APP_CONTINUE;
@@ -80,7 +84,7 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
return YASL_APP_CONTINUE;
}
-static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame_context *ctx)
+static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
{
struct test_state *state = ud;
assert(s == state->surface);
@@ -88,12 +92,12 @@ static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame_context
state->frame_called = true;
state->frame_count++;
- if (ctx->cpu.pixels) {
- size_t count = (size_t)ctx->size.x * ctx->size.y;
+ if (ctx->target.pixels) {
+ size_t count = (size_t)ctx->target.size.x * ctx->target.size.y;
for (size_t i = 0; i < count; i++) {
- ctx->cpu.pixels[i] = 0xff204060;
+ ctx->target.pixels[i] = 0xff204060;
}
- bool ok = yasl_frame_present_cpu(ctx, NULL, 0);
+ bool ok = yasl_frame_present(ctx, NULL, 0);
assert(ok);
}
@@ -106,14 +110,15 @@ static void on_quit(void *ud, struct yasl_display *d)
{
(void)d;
struct test_state *state = ud;
+ assert(!pthread_join(state->worker, NULL));
state->quit_called = true;
}
-static void present_once(void *ud, struct yasl_surface *s, struct yasl_frame_context *ctx)
+static void present_once(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
{
(*(int *)ud)++;
- if (ctx->cpu.pixels)
- assert(yasl_frame_present_cpu(ctx, NULL, 0));
+ if (ctx->target.pixels)
+ assert(yasl_frame_present(ctx, NULL, 0));
yasl_surface_destroy(s);
}
@@ -132,6 +137,43 @@ static void test_window_without_init(void)
assert(frames == 1);
}
+
+static enum yasl_app_result fail_init(void *ud, struct yasl_display *d)
+{
+ (void)ud; (void)d;
+ return YASL_APP_ERROR;
+}
+static void count_quit(void *ud, struct yasl_display *d)
+{
+ (void)d;
+ (*(int *)ud)++;
+}
+static enum yasl_app_result inspect_initial_window(void *ud, struct yasl_display *d)
+{
+ struct yasl_surface *s = yasl_app_window(d);
+ assert(s && yasl_surface_userdata(s) == ud);
+ errno = 0;
+ assert(!yasl_surface_set_callbacks(s, NULL, NULL) && errno == EBUSY);
+ return YASL_APP_CONTINUE;
+}
+static void fail_frame(void *ud, struct yasl_surface *s, struct yasl_frame *frame)
+{
+ (void)ud;
+ assert(frame->surface == s && frame->target.stride_bytes == frame->target.size.x * 4);
+ yasl_app_exit(yasl_surface_display(s), YASL_APP_ERROR);
+}
+static void test_error_lifecycle(void)
+{
+ int quits = 0;
+ assert(yasl_run(&(struct yasl_app_desc){.userdata = &quits,
+ .init = fail_init, .quit = count_quit}) < 0);
+ assert(quits == 1 && errno == EIO);
+ struct yasl_window_spec window = {.size_logical = {160, 120}, .userdata = &quits};
+ assert(yasl_run(&(struct yasl_app_desc){.userdata = &quits, .window = &window,
+ .init = inspect_initial_window, .frame = fail_frame, .quit = count_quit}) < 0);
+ assert(quits == 2 && errno == EIO);
+}
+
int main(void)
{
struct test_state state = {0};
@@ -146,6 +188,9 @@ int main(void)
};
int res = yasl_run(&desc);
+ if (res < 0 && !state.init_called &&
+ (errno == EPERM || errno == EACCES || errno == ENOENT ||
+ errno == ECONNREFUSED || errno == ENODEV)) return 77;
assert(res == 0);
assert(state.init_called);
assert(state.frame_called);
@@ -153,6 +198,7 @@ int main(void)
assert(state.timer_called);
assert(state.quit_called);
test_window_without_init();
+ test_error_lifecycle();
printf("App runner tests passed!\n");
return 0;
}
diff --git a/tests/common/test_events.c b/tests/common/test_events.c
new file mode 100644
index 0000000..c8660cb
--- /dev/null
+++ b/tests/common/test_events.c
@@ -0,0 +1,209 @@
+#include "common/backend.h"
+#include "yasl/app.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct input_queue *queue(struct yasl_surface *s) { return &s->input; }
+static const struct yasl_backend_ops ops = {.surface_input_queue = queue};
+static int released;
+static pthread_t owner;
+static void release(void *payload)
+{
+ assert(pthread_equal(owner, pthread_self()));
+ assert(payload == &released);
+ released++;
+}
+
+static void setup(struct yasl_display *d, struct yasl_surface *a, struct yasl_surface *b)
+{
+ *d = (struct yasl_display){.ops = &ops};
+ *a = (struct yasl_surface){.display = d, .scale_120 = 120};
+ *b = (struct yasl_surface){.display = d, .scale_120 = 120};
+ assert(yasl_events_open(d));
+ assert(yasl_events_register(a));
+ assert(yasl_events_register(b));
+}
+
+static void teardown(struct yasl_display *d, struct yasl_surface *a, struct yasl_surface *b)
+{
+ yasl_events_unregister(a);
+ yasl_events_unregister(b);
+ input_queue_clear(&a->input);
+ input_queue_clear(&b->input);
+ yasl_events_close(d);
+}
+
+static void test_order_and_lifetimes(void)
+{
+ struct yasl_display d;
+ struct yasl_surface a, b;
+ setup(&d, &a, &b);
+ uint64_t aid = yasl_surface_id(&a), bid = yasl_surface_id(&b);
+ assert(aid && bid && aid != bid);
+ yasl_surface_set_userdata(&a, &b);
+ assert(yasl_surface_userdata(&a) == &b);
+ char text[] = "text";
+ assert(input_queue_push(&a.input, &(struct yasl_event){
+ .type = YASL_EVENT_TEXT, .surface = &a, .text = {text, 4}}));
+ memset(text, 'x', 4);
+ yasl_surface_notify_state(&b, YASL_STATE_MAXIMIZED);
+ yasl_surface_notify_configure(&a, (struct yasl_geometry){{640, 480}, 180});
+ yasl_surface_notify_drag(&b, YASL_DRAG_OVER, (struct yasl_vector2i){3, 4});
+ yasl_surface_notify_composition(&a, "hello", 1, 3);
+ yasl_surface_notify_composition(&a, NULL, -1, -1);
+ yasl_surface_complete_paste(&b, 42, YASL_TRANSFER_UNAVAILABLE, NULL, 0);
+ const enum yasl_event_type expected[] = {YASL_EVENT_TEXT, YASL_EVENT_WINDOW_STATE,
+ YASL_EVENT_WINDOW_RESIZE, YASL_EVENT_DRAG, YASL_EVENT_COMPOSITION,
+ YASL_EVENT_COMPOSITION, YASL_EVENT_PASTE};
+ struct yasl_event ev;
+ uint64_t sequence = 0;
+ for (size_t i = 0; i < sizeof(expected) / sizeof(*expected); i++) {
+ assert(yasl_display_poll_event(&d, &ev));
+ assert(ev.type == expected[i] && ev.sequence > sequence && ev.time_ns);
+ sequence = ev.sequence;
+ if (i == 0) assert(!strcmp(ev.text.data, "text") && ev.surface_id == aid);
+ if (i == 2) assert(ev.resize.scale_changed && ev.resize.geometry.size.x == 640);
+ if (i == 4) assert(ev.composition.active && ev.composition.begin == 1 && !strcmp(ev.composition.data, "hello"));
+ if (i == 5) assert(!ev.composition.active && !ev.composition.len);
+ if (i == 6) assert(ev.paste.request_id == 42 && ev.paste.status == YASL_TRANSFER_UNAVAILABLE);
+ }
+ assert(!yasl_display_poll_event(&d, &ev));
+ assert(input_queue_push(&a.input, &(struct yasl_event){
+ .type = YASL_EVENT_TEXT, .surface = &a, .text = {"kept", 4}}));
+ assert(yasl_display_poll_event(&d, &ev));
+ const char *borrowed = ev.text.data;
+ assert(input_queue_push(&a.input, &(struct yasl_event){.type = YASL_EVENT_KEY, .surface = &a}));
+ yasl_events_unregister(&a);
+ assert(!strcmp(borrowed, "kept"));
+ assert(!yasl_display_poll_event(&d, &ev));
+ /* Reusing object memory must never reuse the identity. */
+ assert(yasl_events_register(&a));
+ assert(a.id != aid);
+ errno = 0;
+ assert(!yasl_display_post(&d, &(struct yasl_message){.target_id = aid}));
+ assert(errno == ENOENT);
+ teardown(&d, &a, &b);
+}
+
+struct producer { struct yasl_display *display; uint64_t target; };
+static void *post_worker(void *ud)
+{
+ struct producer *producer = ud;
+ for (uint32_t i = 0; i < 32; i++)
+ assert(yasl_display_post(producer->display, &(struct yasl_message){
+ .target_id = producer->target, .code = i, .payload = &released, .release = release}));
+ return NULL;
+}
+
+static void test_post_cleanup(void)
+{
+ struct yasl_display d;
+ struct yasl_surface a, b;
+ setup(&d, &a, &b);
+ owner = pthread_self();
+ released = 0;
+ pthread_t worker;
+ struct producer producer = {&d, a.id};
+ assert(!pthread_create(&worker, NULL, post_worker, &producer));
+ assert(!pthread_join(worker, NULL));
+ struct yasl_event ev;
+ for (uint32_t i = 0; i < 8; i++) {
+ assert(yasl_display_poll_event(&d, &ev));
+ assert(ev.user.code == i && ev.surface == &a);
+ }
+ assert(released == 7); /* Last event remains borrowed. */
+ yasl_events_unregister(&a);
+ assert(released == 31); /* Undelivered target messages canceled. */
+ assert(!yasl_display_poll_event(&d, &ev));
+ assert(yasl_display_post(&d, &(struct yasl_message){.payload = &released, .release = release}));
+ yasl_events_close(&d);
+ assert(released == 33); /* Pending message and last consumed message. */
+ errno = 0;
+ assert(!yasl_display_post(&d, &(struct yasl_message){.payload = &released, .release = release}));
+ assert(errno == EINVAL && released == 33);
+}
+
+static int damage_count;
+static bool present(struct yasl_surface *s, const struct yasl_framebuffer *target,
+ const struct yasl_damage_list *damage)
+{
+ (void)s; (void)target;
+ damage_count = damage->n;
+ return true;
+}
+static void test_damage(void)
+{
+ struct yasl_backend_ops present_ops = {.surface_present = present};
+ struct yasl_display d = {.ops = &present_ops};
+ struct yasl_surface s = {.display = &d, .frame_generation = 1, .drawing = true};
+ struct yasl_frame frame = {.surface = &s, .target = {.generation = 1, .size = {8, 8}}};
+ s.active_frame = &frame;
+ struct yasl_rect2i rects[YASL_DAMAGE_MAX + 1] = {{0}};
+ assert(yasl_frame_present(&frame, NULL, 0) && damage_count == -1);
+ assert(!yasl_frame_present(&frame, NULL, 0) && errno == EALREADY);
+ s.frame_presented = false;
+ assert(yasl_frame_present(&frame, rects, 0) && damage_count == 0);
+ s.frame_presented = false;
+ assert(yasl_frame_present(&frame, rects, YASL_DAMAGE_MAX + 1) && damage_count == -1);
+ s.frame_presented = false;
+ rects[YASL_DAMAGE_MAX].width = -1;
+ errno = 0;
+ assert(!yasl_frame_present(&frame, rects, YASL_DAMAGE_MAX + 1) && errno == EINVAL);
+ assert(!yasl_frame_present(&frame, NULL, 1));
+ s.drawing = false;
+ assert(!yasl_frame_present(&frame, NULL, 0));
+}
+
+static void test_failures(void)
+{
+ struct yasl_display d;
+ struct yasl_surface a, b;
+ setup(&d, &a, &b);
+ assert(!yasl_surface_set_size(&a, (struct yasl_vector2i){0, 10}) && errno == EINVAL);
+ assert(!yasl_surface_set_size(&a, (struct yasl_vector2i){10, 10}) && errno == ENOTSUP);
+ assert(!yasl_surface_set_fullscreen(&a, true) && errno == ENOTSUP);
+ assert(!yasl_surface_set_maximized(&a, true) && errno == ENOTSUP);
+ assert(!yasl_surface_minimize(&a) && errno == ENOTSUP);
+ a.popup = true;
+ assert(!yasl_surface_minimize(&a) && errno == EINVAL);
+ assert(!yasl_clipboard_set(&d, YASL_CLIPBOARD, "text") && errno == ENOTSUP);
+ assert(!yasl_clipboard_request(&a, YASL_CLIPBOARD) && errno == ENOTSUP);
+ /* Queued native event allocation/validation failure must be observable. */
+ assert(!yasl_events_push(&d, &a, &(struct yasl_event){
+ .type = YASL_EVENT_TEXT, .surface = &a, .text = {"x", SIZE_MAX}}));
+ assert(yasl_events_error(&d) == ENOMEM);
+ teardown(&d, &a, &b);
+}
+
+
+static void test_legacy_poll_does_not_spin(void)
+{
+ struct yasl_display d;
+ struct yasl_surface a, b;
+ setup(&d, &a, &b);
+ yasl_surface_notify_state(&a, YASL_STATE_ACTIVATED);
+ yasl_surface_notify_close(&b);
+ assert(yasl_events_pending(&d));
+ struct yasl_event ev;
+ assert(!yasl_surface_poll_event(&a, &ev));
+ assert(!yasl_events_pending(&d));
+ assert(input_queue_push(&b.input, &(struct yasl_event){.type = YASL_EVENT_KEY, .surface = &b}));
+ assert(!yasl_surface_poll_event(&a, &ev));
+ assert(yasl_surface_poll_event(&b, &ev) && ev.surface == &b);
+ assert(!yasl_events_pending(&d));
+ teardown(&d, &a, &b);
+}
+
+int main(void)
+{
+ test_order_and_lifetimes();
+ test_post_cleanup();
+ test_damage();
+ test_failures();
+ test_legacy_poll_does_not_spin();
+ return 0;
+}
diff --git a/tests/common/test_hello_example.c b/tests/common/test_hello_example.c
index 101e2d4..350b3bb 100644
--- a/tests/common/test_hello_example.c
+++ b/tests/common/test_hello_example.c
@@ -3,7 +3,7 @@
#include "../../examples/hello.c"
#undef main
-static void draw_once(void *ud, struct yasl_surface *surface, struct yasl_frame_context *frame)
+static void draw_once(void *ud, struct yasl_surface *surface, struct yasl_frame *frame)
{
draw(ud, surface, frame);
if (!failed) yasl_surface_destroy(surface);