diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/benchmark.c | 1 | ||||
| -rw-r--r-- | tests/common/test_contracts.c | 9 | ||||
| -rw-r--r-- | tests/common/test_hello_example.c | 20 | ||||
| -rw-r--r-- | tests/common/test_tablet_example.c | 20 | ||||
| -rw-r--r-- | tests/common/test_timers.c | 1 | ||||
| -rw-r--r-- | tests/helpers/run_backends.py | 8 | ||||
| -rw-r--r-- | tests/meson.build | 17 | ||||
| -rw-r--r-- | tests/test_lifecycle.c | 1 | ||||
| -rw-r--r-- | tests/wayland/test_app_input.c | 55 | ||||
| -rw-r--r-- | tests/wayland/test_state.c | 75 | ||||
| -rw-r--r-- | tests/x11/test_state.c | 77 | ||||
| -rw-r--r-- | tests/x11/test_tablet_native.c | 49 |
12 files changed, 317 insertions, 16 deletions
diff --git a/tests/benchmark.c b/tests/benchmark.c index 8cea45c..7431715 100644 --- a/tests/benchmark.c +++ b/tests/benchmark.c @@ -10,7 +10,6 @@ #include "yasl/yasl.h" #include "yasl/loop.h" #include "yasl/timing.h" -#include "yasl/native.h" #include "common/input.h" #if YASL_HAS_WAYLAND #include "yasl/native/wayland.h" diff --git a/tests/common/test_contracts.c b/tests/common/test_contracts.c index ac0065c..ee6fe61 100644 --- a/tests/common/test_contracts.c +++ b/tests/common/test_contracts.c @@ -25,6 +25,14 @@ static void test_timing_clock(void) assert(stats.max_us == 500.0); } +static void test_surface_state(void) +{ + /* Reading reported flags does not require a backend query or display. */ + struct yasl_surface surface = {.state = YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED}; + assert(!yasl_surface_state(NULL)); + assert(yasl_surface_state(&surface) == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); +} + static void test_queue(void) { struct input_queue q = {0}; @@ -326,6 +334,7 @@ static void test_errors(void) int main(void) { test_timing_clock(); + test_surface_state(); test_queue(); test_validation(); test_native(); diff --git a/tests/common/test_hello_example.c b/tests/common/test_hello_example.c new file mode 100644 index 0000000..6e0318f --- /dev/null +++ b/tests/common/test_hello_example.c @@ -0,0 +1,20 @@ +/* Exercise hello's real frame callback; automated exit belongs in the test. */ +#define main hello_example_main +#include "../../examples/hello.c" +#undef main + +static void draw_once(void *ud, struct yasl_surface *surface, struct yasl_frame_context *frame) +{ + draw(ud, surface, frame); + if (!((struct app *)ud)->failed) yasl_surface_destroy(surface); +} + +int main(void) +{ + struct app app = {0}; + struct yasl_app_desc desc = { + .app_id = "org.yasl.test.hello", .userdata = &app, + .init = init, .event = event, .frame = draw_once, + }; + return yasl_run(&desc) || app.failed ? 1 : 0; +} diff --git a/tests/common/test_tablet_example.c b/tests/common/test_tablet_example.c index 15dde9c..77e79f6 100644 --- a/tests/common/test_tablet_example.c +++ b/tests/common/test_tablet_example.c @@ -6,8 +6,20 @@ static void pointer(struct app *app, enum yasl_pointer_kind kind, enum yasl_pointer_device device, int button, int x, int y, uint16_t pressure) { - struct yasl_input_event event = {.kind = YASL_INPUT_MOUSE, .mouse = { - .kind = kind, .device = device, .button = button, .pos = {x, y}, .pen.pressure = pressure}}; + struct yasl_event event = {0}; + if (kind == YASL_PTR_MOTION) { + event.type = YASL_EVENT_MOUSE_MOTION; + event.mouse_motion.pos = (struct yasl_vector2i){x, y}; + event.mouse_motion.device = device; + event.mouse_motion.pen.pressure = pressure; + } else { + event.type = YASL_EVENT_MOUSE_BUTTON; + event.mouse_button.pos = (struct yasl_vector2i){x, y}; + event.mouse_button.device = device; + event.mouse_button.button = button; + event.mouse_button.pressed = kind == YASL_PTR_PRESS; + event.mouse_button.pen.pressure = pressure; + } input(app, &event); } @@ -38,13 +50,13 @@ int main(void) pointer(&app, YASL_PTR_PRESS, YASL_PTR_DEV_ERASER, 0, 80, 160, 65535); assert(app.canvas[(size_t)160 * app.geometry.size.x + 80] == PAPER && app.canvas[(size_t)160 * app.geometry.size.x + 130] == INK); - struct yasl_input_event reset = {.kind = YASL_INPUT_RESET}; + struct yasl_event reset = {.type = YASL_EVENT_INPUT_RESET}; input(&app, &reset); pointer(&app, YASL_PTR_MOTION, YASL_PTR_DEV_ERASER, 0, 130, 160, 65535); assert(!app.down && app.canvas[(size_t)160 * app.geometry.size.x + 130] == INK); pointer(&app, YASL_PTR_PRESS, YASL_PTR_DEV_MOUSE, 0, 300, 200, 0); assert(app.canvas[(size_t)200 * app.geometry.size.x + 300] == INK); - struct yasl_input_event blur = {.kind = YASL_INPUT_FOCUS, .focus.focused = false}; + struct yasl_event blur = {.type = YASL_EVENT_WINDOW_FOCUS, .focus.focused = false}; input(&app, &blur); assert(!app.down); assert(resize_canvas(&app, (struct yasl_geometry){{800, 600}, 240})); diff --git a/tests/common/test_timers.c b/tests/common/test_timers.c index f321b2c..8c92158 100644 --- a/tests/common/test_timers.c +++ b/tests/common/test_timers.c @@ -4,7 +4,6 @@ #include <assert.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> struct test_ctx { diff --git a/tests/helpers/run_backends.py b/tests/helpers/run_backends.py index 70c82fb..76fe8ab 100644 --- a/tests/helpers/run_backends.py +++ b/tests/helpers/run_backends.py @@ -21,7 +21,7 @@ def wait_for(process, ready): def main(): - weston, xwayland, test, timers, tablet, hello, input_x11 = sys.argv[1:] + weston, xwayland, test, timers, tablet, hello, input_x11, interactive = sys.argv[1:] servers = [] with tempfile.TemporaryDirectory(prefix="yasl-backends-") as directory: root = Path(directory) @@ -87,7 +87,11 @@ def main(): subprocess.run([tablet, "--backend", backend, "--smoke-test"], env=env, check=True, timeout=8) print("Test: hello example on", backend, flush=True) - subprocess.run([hello, "--smoke-test"], + subprocess.run([hello], + env=env if backend == "wayland" else x_env, + check=True, timeout=8) + print("Test: interactive example on", backend, flush=True) + subprocess.run([interactive, "--smoke-test"], env=env if backend == "wayland" else x_env, check=True, timeout=8) except BaseException: diff --git a/tests/meson.build b/tests/meson.build index 26bb5cb..951b30e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -138,6 +138,10 @@ tablet_example_test = executable('yasl-tablet-example-test', 'common/test_tablet test('tablet-example', tablet_example_test) if has_x11 + state_x11_test = executable('yasl-state-x11-test', 'x11/test_state.c', + include_directories: inc, dependencies: yasl_dep, + ) + test('state-x11', state_x11_test) tablet_x11_test = executable('yasl-tablet-x11-test', 'x11/test_tablet.c', include_directories: inc, dependencies: yasl_dep, ) @@ -156,6 +160,10 @@ elif get_option('x11_tablet_tests') endif if has_wayland + state_wayland_test = executable('yasl-state-wayland-test', 'wayland/test_state.c', + include_directories: inc, dependencies: yasl_dep, + ) + test('state-wayland', state_wayland_test) clipboard_wayland_test = executable('yasl-clipboard-wayland-test', 'wayland/test_clipboard.c', include_directories: inc, dependencies: yasl_dep, ) @@ -192,8 +200,12 @@ benchmark_exe = executable( benchmark('performance', benchmark_exe) if get_option('integration_tests') + hello_example_test = executable('yasl-hello-example-test', 'common/test_hello_example.c', + dependencies: shared_dep, + ) if has_cocoa - test('hello-cocoa-window', hello_example, + test('hello-cocoa-window', hello_example_test, timeout: 20) + test('interactive-cocoa-window', interactive_example, args: ['--smoke-test'], timeout: 20, ) native_test = executable('yasl-native-test', 'cocoa/test_native.c', @@ -235,7 +247,8 @@ if get_option('integration_tests') xwayland = find_program('Xwayland') test('backends', python, args: [files('helpers/run_backends.py'), weston, xwayland, - backend_test, timer_test, tablet_example, hello_example, input_x11_test], + backend_test, timer_test, tablet_example, hello_example_test, input_x11_test, + interactive_example], timeout: 60, ) endif diff --git a/tests/test_lifecycle.c b/tests/test_lifecycle.c index c54abd0..00d1264 100644 --- a/tests/test_lifecycle.c +++ b/tests/test_lifecycle.c @@ -1,5 +1,4 @@ #include "yasl/loop.h" -#include "yasl/timing.h" #include "yasl/yasl.h" #include <assert.h> #include <poll.h> diff --git a/tests/wayland/test_app_input.c b/tests/wayland/test_app_input.c index 2cacb5e..c8c5687 100644 --- a/tests/wayland/test_app_input.c +++ b/tests/wayland/test_app_input.c @@ -9,6 +9,8 @@ struct test_state { int keys; + int buttons, motions, resets; + int end; }; static void push_key(struct yasl_surface *s) @@ -23,10 +25,32 @@ static void push_key(struct yasl_surface *s) static int on_init(void *ud, struct yasl_display *d) { - (void)ud; + struct test_state *state = ud; + if (state->end) { + struct wayland_display *display = (struct wayland_display *)d; + if (state->end == 1) + display->cbs->quit(display->ud); + else + display->cbs->lost(display->ud); + return YASL_APP_CONTINUE; + } struct yasl_surface *s = yasl_surface_create_toplevel(d, &(struct yasl_window_spec){ .title = "App Input Test", .size = {64, 64} }); assert(s); + struct input_queue *queue = &((struct wayland_surface *)s)->input; + input_queue_clear(queue); + struct yasl_input_event event = {.surface = s, .kind = YASL_INPUT_MOUSE, + .mouse = {.kind = YASL_PTR_PRESS, .device = YASL_PTR_DEV_ERASER, + .pos = {12, 34}, .pen = {.pressure = 50000, .tilt_x = -20, .tilt_y = 30}}}; + assert(input_queue_push(queue, &event)); + event.mouse.kind = YASL_PTR_MOTION; + event.mouse.pos = (struct yasl_vector2i){15, 39}; + assert(input_queue_push(queue, &event)); + struct yasl_input_event reset = {.surface = s, .kind = YASL_INPUT_RESET}; + assert(input_queue_push(queue, &reset)); + event.mouse.device = YASL_PTR_DEV_PEN; + event.mouse.pos = (struct yasl_vector2i){100, 200}; + assert(input_queue_push(queue, &event)); push_key(s); push_key(s); return YASL_APP_CONTINUE; @@ -35,7 +59,26 @@ static int on_init(void *ud, struct yasl_display *d) static int on_event(void *ud, const struct yasl_event *ev) { struct test_state *state = ud; - if (ev->type == YASL_EVENT_KEY) { + if (ev->type == YASL_EVENT_MOUSE_BUTTON) { + assert(ev->mouse_button.device == YASL_PTR_DEV_ERASER); + assert(ev->mouse_button.pressed && ev->mouse_button.button == 0); + assert(ev->mouse_button.pos.x == 12 && ev->mouse_button.pos.y == 34); + assert(ev->mouse_button.pen.pressure == 50000); + assert(ev->mouse_button.pen.tilt_x == -20 && ev->mouse_button.pen.tilt_y == 30); + state->buttons++; + } else if (ev->type == YASL_EVENT_MOUSE_MOTION) { + if (!state->motions) { + assert(ev->mouse_motion.device == YASL_PTR_DEV_ERASER); + assert(ev->mouse_motion.delta.x == 3 && ev->mouse_motion.delta.y == 5); + } else { + assert(ev->mouse_motion.device == YASL_PTR_DEV_PEN); + assert(!ev->mouse_motion.delta.x && !ev->mouse_motion.delta.y); + } + assert(ev->mouse_motion.pen.pressure == 50000); + state->motions++; + } else if (ev->type == YASL_EVENT_INPUT_RESET) { + state->resets++; + } else if (ev->type == YASL_EVENT_KEY) { state->keys++; yasl_surface_destroy(ev->surface); } @@ -56,5 +99,13 @@ int main(void) }); assert(res == 0); assert(state.keys == 1); + assert(state.buttons == 1 && state.motions == 2 && state.resets == 1); + for (int end = 1; end <= 2; end++) { + state = (struct test_state){.end = end}; + res = yasl_run(&(struct yasl_app_desc){ + .backend = YASL_BACKEND_WAYLAND, .userdata = &state, .init = on_init, + }); + assert(end == 1 ? res == 0 : res < 0); + } return 0; } diff --git a/tests/wayland/test_state.c b/tests/wayland/test_state.c new file mode 100644 index 0000000..2da26e5 --- /dev/null +++ b/tests/wayland/test_state.c @@ -0,0 +1,75 @@ +#include "../../src/platform/wayland/shell.c" +#include <assert.h> + +struct notifications { + int count; + uint32_t state; +}; + +static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud) +{ + struct notifications *notifications = ud; + assert(yasl_surface_state(surface) == state); + notifications->count++; + notifications->state = state; +} + +static void configure(struct wayland_surface *surface, uint32_t *states, size_t count) +{ + struct wl_array array = {.size = count * sizeof(*states), .data = states}; + toplevel_configure(surface, NULL, 640, 480, &array); + assert(surface->req_width == 640 && surface->req_height == 480); +} + +int main(void) +{ + struct notifications notifications = {0}; + struct wayland_surface surface = {.base.state = YASL_STATE_CSD}; + wayland_surface_set_callbacks(&surface, &(struct yasl_window_cbs){ + .state = state_changed}, ¬ifications); + assert(yasl_surface_state(&surface.base) == YASL_STATE_CSD); + + uint32_t fullscreen[] = {XDG_TOPLEVEL_STATE_FULLSCREEN, + XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_FULLSCREEN, 31, UINT32_MAX}; + configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); + assert(notifications.count == 1); + assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); + assert(notifications.count == 1); + + /* Decoration changes preserve the other flags and do not notify twice. */ + toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + assert(notifications.count == 2); + assert(notifications.state == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + assert(notifications.count == 2); + + uint32_t maximized[] = {XDG_TOPLEVEL_STATE_MAXIMIZED, XDG_TOPLEVEL_STATE_SUSPENDED}; + configure(&surface, maximized, sizeof(maximized) / sizeof(*maximized)); + assert(notifications.count == 3); + assert(notifications.state == (YASL_STATE_MAXIMIZED | YASL_STATE_SUSPENDED)); + toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); + assert(notifications.count == 4); + assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_MAXIMIZED | YASL_STATE_SUSPENDED)); + + /* Each tiled edge maps to the same portable flag; changing edges alone + * leaves the reported state unchanged. */ + uint32_t edges[] = {XDG_TOPLEVEL_STATE_TILED_LEFT, XDG_TOPLEVEL_STATE_TILED_RIGHT, + XDG_TOPLEVEL_STATE_TILED_TOP, XDG_TOPLEVEL_STATE_TILED_BOTTOM}; + for (size_t i = 0; i < sizeof(edges) / sizeof(*edges); i++) { + configure(&surface, &edges[i], 1); + assert(notifications.count == 5); + assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_TILED)); + } + configure(&surface, edges, 0); + assert(notifications.count == 6); + assert(notifications.state == YASL_STATE_CSD); + + /* State still advances when applications have not installed callbacks. */ + wayland_surface_set_callbacks(&surface, NULL, NULL); + configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); + assert(yasl_surface_state(&surface.base) == + (YASL_STATE_CSD | YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + assert(notifications.count == 6); + return 0; +} diff --git a/tests/x11/test_state.c b/tests/x11/test_state.c new file mode 100644 index 0000000..abdda95 --- /dev/null +++ b/tests/x11/test_state.c @@ -0,0 +1,77 @@ +/* Supply native window-state properties without requiring an X server. */ +#define XGetWindowProperty state_property +#include "../../src/platform/x11/window.c" +#undef XGetWindowProperty + +static Atom properties[3]; +static unsigned long property_count; +static int notifications; +static uint32_t reported_state; + +int state_property(Display *display, Window window, Atom property, + long offset, long length, Bool delete_property, Atom type, + Atom *actual, int *format, unsigned long *count, unsigned long *left, + unsigned char **data) +{ + assert(display && window == 42 && property == 10); + assert(!offset && length >= 3 && !delete_property && type == XA_ATOM); + *actual = XA_ATOM; + *format = 32; + *count = property_count; + *left = 0; + *data = NULL; + if (property_count) { + *data = malloc(property_count * sizeof(*properties)); + assert(*data); + memcpy(*data, properties, property_count * sizeof(*properties)); + } + return Success; +} + +static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud) +{ + (void)ud; + assert(yasl_surface_state(surface) == state); + reported_state = state; + notifications++; +} + +int main(void) +{ + struct x11_display display = {.base.ops = &yasl_x11_backend, + .net_state = 10, .fullscreen = 20, .maximized_h = 21, .maximized_v = 22}; + display.native = (Display *)&display; + struct x11_surface surface = {.base = {.display = &display.base, + .state = YASL_STATE_ACTIVATED}, .window = 42}; + set_callbacks(&surface.base, &(struct yasl_window_cbs){.state = state_changed}, NULL); + + properties[0] = display.fullscreen; + property_count = 1; + update_state(&surface); + assert(notifications == 1); + assert(reported_state == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + update_state(&surface); + assert(notifications == 1); + + /* Both maximized axes are required; activation survives property changes. */ + properties[0] = display.maximized_h; + update_state(&surface); + assert(notifications == 2 && reported_state == YASL_STATE_ACTIVATED); + properties[1] = display.maximized_v; + property_count = 2; + update_state(&surface); + assert(notifications == 3); + assert(reported_state == (YASL_STATE_MAXIMIZED | YASL_STATE_ACTIVATED)); + properties[2] = display.fullscreen; + property_count = 3; + update_state(&surface); + assert(notifications == 4); + assert(reported_state == (YASL_STATE_FULLSCREEN | YASL_STATE_MAXIMIZED | YASL_STATE_ACTIVATED)); + + property_count = 0; + update_state(&surface); + assert(notifications == 5 && reported_state == YASL_STATE_ACTIVATED); + update_state(&surface); + assert(notifications == 5); + return 0; +} diff --git a/tests/x11/test_tablet_native.c b/tests/x11/test_tablet_native.c index f12ec7f..078606a 100644 --- a/tests/x11/test_tablet_native.c +++ b/tests/x11/test_tablet_native.c @@ -55,6 +55,49 @@ static int connect_tool(const char *path) return fd; } +static void native_draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud) +{ + struct yasl_frame_context context = { + .surface = surface, .size = frame->target.size, + .scale_120 = yasl_surface_geometry(surface).scale_120, + .cpu = {.pixels = frame->target.pixels, + .stride_bytes = frame->target.size.x * (int)sizeof(uint32_t)}, + .internal_handle = frame, + }; + draw(ud, surface, &context); +} + +static void native_configure(struct yasl_surface *surface, struct yasl_geometry geometry, void *ud) +{ + input(ud, &(struct yasl_event){.surface = surface, .type = YASL_EVENT_WINDOW_RESIZE, + .resize = {.size = geometry.size, .scale_120 = geometry.scale_120}}); +} + +static void native_input(struct app *app, const struct yasl_input_event *raw) +{ + struct yasl_event event = {.surface = raw->surface}; + if (raw->kind == YASL_INPUT_RESET) event.type = YASL_EVENT_INPUT_RESET; + else if (raw->kind == YASL_INPUT_FOCUS) { + event.type = YASL_EVENT_WINDOW_FOCUS; + event.focus.focused = raw->focus.focused; + } else if (raw->kind == YASL_INPUT_MOUSE) { + if (raw->mouse.kind == YASL_PTR_MOTION) { + event.type = YASL_EVENT_MOUSE_MOTION; + event.mouse_motion.pos = raw->mouse.pos; + event.mouse_motion.device = raw->mouse.device; + event.mouse_motion.pen = raw->mouse.pen; + } else if (raw->mouse.kind == YASL_PTR_PRESS || raw->mouse.kind == YASL_PTR_RELEASE) { + event.type = YASL_EVENT_MOUSE_BUTTON; + event.mouse_button.pos = raw->mouse.pos; + event.mouse_button.device = raw->mouse.device; + event.mouse_button.button = raw->mouse.button; + event.mouse_button.pressed = raw->mouse.kind == YASL_PTR_PRESS; + event.mouse_button.pen = raw->mouse.pen; + } else if (raw->mouse.kind == YASL_PTR_LEAVE) event.type = YASL_EVENT_MOUSE_LEAVE; + } + input(app, &event); +} + static void pump(int fd, struct yasl_display *display, struct yasl_surface *surface, struct app *app) { xf86ITEventWaitForSync sync = {{sizeof(sync), XF86IT_EVENT_WAIT_FOR_SYNC}}; @@ -68,7 +111,7 @@ static void pump(int fd, struct yasl_display *display, struct yasl_surface *surf assert(yasl_display_dispatch(display, 0) == 0); struct yasl_input_event event; while (yasl_surface_input_poll(surface, &event)) { - input(app, &event); + native_input(app, &event); yasl_surface_schedule_redraw(surface); } yasl_display_flush(display); @@ -99,14 +142,14 @@ static void set_eraser_property(Display *display) int main(int argc, char **argv) { assert(argc == 3); /* pen socket, eraser socket */ - struct app app = {.running = true, .tablet_supported = true, .backend = YASL_BACKEND_X11}; + struct app app = {.tablet_supported = true, .backend = YASL_BACKEND_X11}; struct yasl_display_spec ds = {.backend = YASL_BACKEND_X11}; struct yasl_display *display = yasl_display_open(NULL, &ds); assert(display && (yasl_display_caps(display) & YASL_CAP_TABLET)); struct yasl_window_spec ss = {.title = "YASL native pressure test", .size = {500, 400}}; struct yasl_surface *surface = yasl_surface_create_toplevel(display, &ss); assert(surface); - struct yasl_window_cbs cbs = {.draw = draw, .configure = configure}; + struct yasl_window_cbs cbs = {.draw = native_draw, .configure = native_configure}; yasl_surface_set_callbacks(surface, &cbs, &app); yasl_surface_schedule_redraw(surface); Display *x = yasl_display_get_native_handle(display); |
