#define _DEFAULT_SOURCE #include "yasl/app.h" #include #include #include #include #include struct test_state { struct yasl_display *display; struct yasl_surface *surface; bool init_called; bool frame_called; bool user_event_called; bool timer_called; 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_display_post(state->display, &(struct yasl_message){ .target_id = state->target_id, .code = 42, .payload = (void *)0x1234}); assert(ok); return NULL; } static uint64_t on_timer(void *ud, yasl_timer_id id, uint64_t interval_ns) { (void)id; (void)interval_ns; struct test_state *state = ud; state->timer_called = true; yasl_surface_request_frame(state->surface); return 0; /* one-shot */ } static enum yasl_app_result on_init(void *ud, struct yasl_display *d) { struct test_state *state = ud; state->display = d; state->init_called = true; struct yasl_window_spec spec = { .title = "App Runner Test", .size = {320, 240}, }; state->surface = yasl_surface_create_toplevel(d, &spec); assert(state->surface != NULL); yasl_surface_set_pacing(state->surface, YASL_PACING_ON_DEMAND); yasl_surface_set_pointer_mode(state->surface, YASL_POINTER_RELATIVE); yasl_surface_set_pointer_mode(state->surface, YASL_POINTER_NORMAL); state->timer_id = yasl_loop_add_timer(yasl_display_loop(d), YASL_MS_TO_NS(20), on_timer, state); assert(state->timer_id > 0); state->target_id = yasl_surface_id(state->surface); int err = pthread_create(&state->worker, NULL, worker_thread, state); assert(err == 0); yasl_surface_request_frame(state->surface); return YASL_APP_CONTINUE; } static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev) { struct test_state *state = ud; if (ev->type == YASL_EVENT_USER) { assert(ev->user.code == 42); assert(ev->user.payload == (void *)0x1234); state->user_event_called = true; yasl_surface_request_frame(state->surface); } else if (ev->type == YASL_EVENT_WINDOW_CLOSE) { return YASL_APP_QUIT; } return YASL_APP_CONTINUE; } static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx) { struct test_state *state = ud; assert(s == state->surface); assert(ctx->surface == s); state->frame_called = true; state->frame_count++; 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->target.pixels[i] = 0xff204060; } bool ok = yasl_frame_present(ctx, NULL, 0); assert(ok); } if (state->user_event_called) { yasl_surface_destroy(state->surface); } } 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 *ctx) { (*(int *)ud)++; if (ctx->target.pixels) assert(yasl_frame_present(ctx, NULL, 0)); yasl_surface_destroy(s); } /* desc.window replaces init: the runner creates the toplevel and draws it. */ static void test_window_without_init(void) { int frames = 0; struct yasl_window_spec window = {.title = "App Runner Window", .size = {160, 120}}; int res = yasl_run(&(struct yasl_app_desc){ .app_id = "org.yasl.test.app.window", .userdata = &frames, .window = &window, .frame = present_once, }); assert(res == 0); 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}; struct yasl_app_desc desc = { .app_id = "org.yasl.test.app", .backend = YASL_BACKEND_AUTO, .userdata = &state, .init = on_init, .event = on_event, .frame = on_frame, .quit = on_quit, }; 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); assert(state.user_event_called); assert(state.timer_called); assert(state.quit_called); test_window_without_init(); test_error_lifecycle(); printf("App runner tests passed!\n"); return 0; }