From 3b8af02be94a510e385f44d58c35c86bb89424a6 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Mon, 12 Oct 2026 08:41:51 +0300 Subject: refactor: simplify public API and isolate native extensions Separate loop/timer and native interoperability APIs from the portable surface header. Unify dispatch, geometry queries, and timer callbacks; keep framebuffer bookkeeping and frame clocks private, and hide implementation symbols. Update backends, documentation, examples, and API/integration coverage. Verify exports, strict C/C++ installed consumers, and normal plus ASan/UBSan suites. --- tests/cocoa/test_native.c | 12 +++++-- tests/cocoa/test_ownership.m | 4 +++ tests/common/test_backends.c | 20 +++++++---- tests/common/test_contracts.c | 78 +++++++++++++++++++++++++++++++++++++++--- tests/common/test_timers.c | 29 ++++++++-------- tests/helpers/check_exports.py | 42 +++++++++++++++++++++++ tests/helpers/run_backends.py | 9 ++++- tests/meson.build | 51 ++++++++++++++++++++++++--- tests/test_api.cpp | 27 +++++++++++++++ tests/test_lifecycle.c | 22 +++--------- tests/wayland/test_input.c | 25 ++++++++++++++ 11 files changed, 266 insertions(+), 53 deletions(-) create mode 100644 tests/helpers/check_exports.py create mode 100644 tests/test_api.cpp (limited to 'tests') diff --git a/tests/cocoa/test_native.c b/tests/cocoa/test_native.c index 41df79a..0c101dd 100644 --- a/tests/cocoa/test_native.c +++ b/tests/cocoa/test_native.c @@ -1,4 +1,6 @@ #define _POSIX_C_SOURCE 200809L +#include "yasl/native.h" +#include "yasl/loop.h" #include "yasl/yasl.h" #include @@ -16,20 +18,24 @@ static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *u assert(surface == state->surface && frame->surface == surface); assert(yasl_surface_display(surface) == state->display); assert(frame->target.pixels); - struct yasl_vector2i size = yasl_surface_size(surface); + struct yasl_native_frame native; + assert(yasl_surface_get_native_frame(surface, &frame->target, &native)); + assert(native.drawable); + struct yasl_vector2i size = yasl_surface_geometry(surface).size; assert(size.x == frame->target.size.x && size.y == frame->target.size.y); for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0; struct yasl_damage_list damage = {.n = YASL_DAMAGE_MAX + 1}; assert(!yasl_surface_present(surface, &frame->target, &damage)); damage.n = -1; struct yasl_framebuffer invalid = frame->target; - invalid.buffer++; + invalid.size.x++; assert(!yasl_surface_present(surface, &invalid, &damage)); invalid = frame->target; invalid.generation--; assert(!yasl_surface_present(surface, &invalid, &damage)); assert(!yasl_surface_present(surface, &frame->target, NULL)); assert(yasl_surface_present(surface, &frame->target, &damage)); + assert(!yasl_surface_get_native_frame(surface, &frame->target, &native)); assert(!yasl_surface_present(surface, &frame->target, &damage)); state->draws++; yasl_surface_destroy(surface); @@ -51,7 +57,7 @@ static void test_window(enum yasl_backend request, bool null_spec) yasl_surface_schedule_redraw(state.surface); uint64_t deadline = yasl_loop_now_ns() + 3000000000ull; while (!state.draws && yasl_loop_now_ns() < deadline) { - yasl_display_dispatch(state.display); + yasl_display_dispatch(state.display, 0); yasl_display_flush(state.display); nanosleep(&(struct timespec){.tv_nsec = 1000000}, NULL); } diff --git a/tests/cocoa/test_ownership.m b/tests/cocoa/test_ownership.m index aa75b1c..168a47b 100644 --- a/tests/cocoa/test_ownership.m +++ b/tests/cocoa/test_ownership.m @@ -1,3 +1,4 @@ +#include "yasl/native.h" #import #include "yasl/yasl.h" #include @@ -11,6 +12,9 @@ int main(void) struct yasl_surface_spec spec = {.title = "Ownership test", .size = {160, 100}}; struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec); assert(s); + assert(yasl_display_get_native_handle(d) == (void *)NSApp); + NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(s); + assert([window isKindOfClass:[NSWindow class]] && window.contentView); struct yasl_input_event event; while (yasl_surface_input_poll(s, &event)) {} @autoreleasepool { diff --git a/tests/common/test_backends.c b/tests/common/test_backends.c index 3372290..4f49765 100644 --- a/tests/common/test_backends.c +++ b/tests/common/test_backends.c @@ -1,4 +1,6 @@ #define _POSIX_C_SOURCE 200809L +#include "yasl/native.h" +#include "yasl/loop.h" #include "yasl/yasl.h" #include @@ -35,7 +37,7 @@ static int tick(int fd, short events, void *ud) struct pump *p = ud; for (int i = 0; i < 2; i++) { if (p->displays[i]) { - yasl_display_dispatch(p->displays[i]); + yasl_display_dispatch(p->displays[i], 0); yasl_display_flush(p->displays[i]); } } @@ -63,9 +65,11 @@ static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) assert(s == w->surface); assert(frame->surface == s); assert(yasl_surface_display(s) == w->display); - struct yasl_vector2i size = yasl_surface_size(s); + struct yasl_vector2i size = yasl_surface_geometry(s).size; assert(size.x == frame->target.size.x && size.y == frame->target.size.y); assert(frame->target.pixels); + struct yasl_native_frame native = {0}; + assert(!yasl_surface_get_native_frame(s, &frame->target, &native)); for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0; struct yasl_damage_list damage = {.n = YASL_DAMAGE_MAX + 1}; assert(!yasl_surface_present(s, &frame->target, &damage)); @@ -76,13 +80,13 @@ static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) assert(!yasl_surface_present(s, &frame->target, &damage)); damage.n = -1; struct yasl_framebuffer invalid = frame->target; - invalid.buffer++; + invalid.size.x++; assert(!yasl_surface_present(s, &invalid, &damage)); invalid = frame->target; invalid.generation--; assert(!yasl_surface_present(s, &invalid, &damage)); invalid = frame->target; - invalid.native_drawable = (void *)(uintptr_t)1; + invalid.pixels = (uint32_t *)(uintptr_t)1; assert(!yasl_surface_present(s, &invalid, &damage)); assert(!yasl_surface_present(s, &frame->target, NULL)); assert(yasl_surface_present(s, &frame->target, &damage)); @@ -95,9 +99,9 @@ static void configure(struct yasl_surface *s, struct yasl_geometry geometry, voi { struct window *w = ud; assert(w->surface == s); - struct yasl_vector2i size = yasl_surface_size(s); + struct yasl_vector2i size = yasl_surface_geometry(s).size; assert(geometry.size.x == size.x && geometry.size.y == size.y); - assert(geometry.scale_120 == yasl_surface_scale_120(s)); + assert(geometry.scale_120 == yasl_surface_geometry(s).scale_120); w->configures++; if (w->destroy_on_configure) { yasl_surface_destroy(s); w->surface = NULL; } } @@ -129,6 +133,8 @@ static struct window create(struct yasl_display *d) struct yasl_surface_spec spec = {.title = "Backend test", .size = {160, 100}}; struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec); assert(s); + assert(yasl_display_get_native_handle(d)); + assert(yasl_surface_get_native_handle(s)); assert(yasl_surface_display(s) == d); assert(yasl_surface_timing(s).source == YASL_TIMING_UNKNOWN); return (struct window){.surface = s, .display = d}; @@ -152,7 +158,7 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct XFlush(native); pump(loop, d, NULL); assert(w->configures > 0); - struct yasl_vector2i size = yasl_surface_size(w->surface); + struct yasl_vector2i size = yasl_surface_geometry(w->surface).size; assert(size.x == 200 && size.y == 120); XEvent key = {0}; key.xkey = (XKeyEvent){.type = KeyPress, .display = native, .window = window, diff --git a/tests/common/test_contracts.c b/tests/common/test_contracts.c index 65eebcd..4e22de1 100644 --- a/tests/common/test_contracts.c +++ b/tests/common/test_contracts.c @@ -1,5 +1,6 @@ #include "common/backend.h" #include "common/input.h" +#include "common/timing.h" #include #include @@ -7,6 +8,23 @@ #include #include +static void test_timing_clock(void) +{ + struct yasl_frame_clock clock = {.refresh_ns = YASL_REFRESH_DEFAULT_NS}; + yasl_clock_presented(&clock, 1000000000, true); + assert(clock.last_present_ns == 1000000000); + uint64_t next = yasl_clock_next_present_ns(&clock); + assert(next > 1000000000); + assert(yasl_clock_extend_ms(1000, 1000000000) == 1000000000); + assert(yasl_clock_frame_timestamp_ns(&clock, next) == next); + + yasl_clock_note_resize(&clock, 500000); + struct yasl_resize_stats stats; + yasl_clock_resize_stats(&clock, &stats); + assert(stats.count == 1); + assert(stats.max_us == 500.0); +} + static void test_queue(void) { struct input_queue q = {0}; @@ -96,8 +114,56 @@ static void copy(struct yasl_display *d, enum yasl_selection sel, const char *te { (void)d; (void)sel; (void)text; copies++; } static void paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *s) { (void)d; (void)sel; (void)s; pastes++; } -static struct yasl_surface *create(struct yasl_display *d, const struct yasl_surface_spec *spec) -{ (void)d; (void)spec; creates++; return NULL; } +static struct yasl_surface *create(struct yasl_display *d, const struct yasl_surface_spec *spec, void *graphics_device) +{ (void)d; (void)spec; (void)graphics_device; creates++; return NULL; } + +static void *native_device; +static int native_queries; +static struct yasl_surface native_surface; +static uint32_t native_caps(const struct yasl_display *d) +{ (void)d; return YASL_CAP_NATIVE_GRAPHICS; } +static struct yasl_surface *native_create(struct yasl_display *d, + const struct yasl_surface_spec *spec, void *device) +{ + (void)spec; + native_device = device; + native_surface = (struct yasl_surface){.display = d, .frame_generation = 1}; + return &native_surface; +} +static bool native_frame(struct yasl_surface *s, const struct yasl_framebuffer *fb, + struct yasl_native_frame *out) +{ + (void)s; (void)fb; + native_queries++; + *out = (struct yasl_native_frame){.drawable = native_device}; + return true; +} + +static void test_native(void) +{ + const struct yasl_backend_ops ops = {.display_caps = native_caps, + .surface_create_toplevel = native_create, .surface_get_native_frame = native_frame}; + struct yasl_display d = {.ops = &ops}; + struct yasl_surface_spec spec = {.size = {100, 80}}; + assert(yasl_surface_create_toplevel(&d, &spec) == &native_surface); + assert(!native_device); + assert(yasl_surface_create_native(&d, &spec, &d) == &native_surface); + assert(native_device == &d); + + struct yasl_framebuffer fb = {.generation = 1, .size = {100, 80}}; + struct yasl_native_frame out = {0}; + assert(!yasl_surface_get_native_frame(NULL, &fb, &out)); + assert(!yasl_surface_get_native_frame(&native_surface, NULL, &out)); + assert(!yasl_surface_get_native_frame(&native_surface, &fb, NULL)); + fb.generation = 0; + assert(!yasl_surface_get_native_frame(&native_surface, &fb, &out)); + fb.generation = 2; + assert(!yasl_surface_get_native_frame(&native_surface, &fb, &out)); + assert(!native_queries && !out.drawable); + fb.generation = 1; + assert(yasl_surface_get_native_frame(&native_surface, &fb, &out)); + assert(native_queries == 1 && out.drawable == &d); +} static void test_validation(void) { @@ -143,9 +209,9 @@ static void test_validation(void) yasl_display_copy(&d, YASL_CLIPBOARD, "yes"); yasl_display_paste(&d, YASL_CLIPBOARD, &s); assert(copies == 1 && pastes == 1); - struct yasl_surface_spec spec = {.size = {100, 80}, .graphics_device = &d}; - assert(!yasl_surface_create_toplevel(&d, &spec) && errno == ENOTSUP); - spec.graphics_device = NULL; + struct yasl_surface_spec spec = {.size = {100, 80}}; + assert(!yasl_surface_create_native(&d, &spec, &d) && errno == ENOTSUP); + assert(!yasl_surface_create_native(&d, &spec, NULL) && errno == EINVAL); spec.size.x = 0; assert(!yasl_surface_create_toplevel(&d, &spec) && errno == EINVAL); assert(!creates); @@ -168,8 +234,10 @@ static void test_scale(void) int main(void) { + test_timing_clock(); test_queue(); test_validation(); + test_native(); test_scale(); return 0; } diff --git a/tests/common/test_timers.c b/tests/common/test_timers.c index 9b6499d..d02bfe9 100644 --- a/tests/common/test_timers.c +++ b/tests/common/test_timers.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include "common/loop.h" #include "yasl/yasl.h" #include @@ -95,18 +96,18 @@ static uint64_t killer_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns return 0; } -/* 6. Millisecond variant timer. */ -static uint32_t ms_timer_cb(void *ud, yasl_timer_id id, uint32_t interval_ms) +/* 6. Millisecond convenience conversion uses the same timer callback. */ +static uint64_t ms_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns) { struct test_ctx *ctx = ud; assert(id == ctx->id); - assert(interval_ms == 5); + assert(interval_ns == YASL_MS_TO_NS(5)); ctx->count++; if (ctx->count >= 3) { ctx->done = true; return 0; } - return interval_ms; + return interval_ns; } static void test_loop_timers(void) @@ -185,9 +186,9 @@ static void test_loop_timers(void) assert(ctx6.count == 1); yasl_loop_step(l, 20); - /* Test 7: Millisecond API */ + /* Test 7: Millisecond conversion */ struct test_ctx ctx7 = { .loop = l }; - ctx7.id = yasl_loop_add_timer_ms(l, 5, ms_timer_cb, &ctx7); + ctx7.id = yasl_loop_add_timer(l, YASL_MS_TO_NS(5), ms_timer_cb, &ctx7); assert(ctx7.id > 0); while (!ctx7.done) { yasl_loop_step(l, 50); @@ -239,14 +240,14 @@ static void test_surface_timers(void) }; struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec); assert(s); - assert(yasl_surface_loop(s) == loop); + assert(yasl_display_loop(yasl_surface_display(s)) == loop); - /* Display timers */ + /* Application timers use the display's loop. */ struct test_ctx dctx = {0}; - dctx.id = yasl_display_add_timer(d, 2000000ull, oneshot_cb, &dctx); + dctx.id = yasl_loop_add_timer(yasl_display_loop(d), 2000000ull, oneshot_cb, &dctx); assert(dctx.id > 0); while (dctx.count == 0) { - yasl_display_dispatch_timeout(d, 50); + yasl_display_dispatch(d, 50); } assert(dctx.count == 1); @@ -255,12 +256,12 @@ static void test_surface_timers(void) sctx.id = yasl_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx); assert(sctx.id > 0); while (sctx.count < 3) { - yasl_display_dispatch_timeout(d, 50); + yasl_display_dispatch(d, 50); } assert(sctx.count >= 3); assert(yasl_surface_del_timer(s, sctx.id)); int count_before = sctx.count; - yasl_display_dispatch_timeout(d, 10); + yasl_display_dispatch(d, 10); assert(sctx.count == count_before); /* Surface timer auto-cleanup on surface destruction */ @@ -269,7 +270,7 @@ static void test_surface_timers(void) assert(sctx2.id > 0); yasl_surface_destroy(s); /* Step display/loop; timer must not fire on dead surface */ - yasl_display_dispatch_timeout(d, 10); + yasl_display_dispatch(d, 10); assert(sctx2.count == 0); /* Surface suicide from timer callback */ @@ -279,7 +280,7 @@ static void test_surface_timers(void) sctx3.id = yasl_surface_add_timer(s, 2000000ull, surface_suicide_cb, &sctx3); assert(sctx3.id > 0); while (sctx3.count == 0) { - yasl_display_dispatch_timeout(d, 50); + yasl_display_dispatch(d, 50); } assert(sctx3.count == 1); assert(sctx3.surface == NULL); diff --git a/tests/helpers/check_exports.py b/tests/helpers/check_exports.py new file mode 100644 index 0000000..0d10014 --- /dev/null +++ b/tests/helpers/check_exports.py @@ -0,0 +1,42 @@ +"""Require the shared library to export exactly the public header functions.""" + +import argparse +from pathlib import Path +import re +import subprocess + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--allow-export', action='append', default=[]) + parser.add_argument('nm') + parser.add_argument('library') + parser.add_argument('headers', nargs='+') + args = parser.parse_args() + declarations = re.compile( + r"^[^\n;{}]*\b(yasl_\w+)\s*\([^;{}]*\);", re.MULTILINE + ) + expected = set() + for header in args.headers: + source = re.sub(r"/\*.*?\*/|//[^\n]*", "", Path(header).read_text(), flags=re.DOTALL) + expected.update(declarations.findall(source)) + if not expected: + raise SystemExit("No public function declarations found") + + output = subprocess.check_output( + [args.nm, "-D", "--defined-only", "--format=posix", args.library], text=True + ) + exported = {line.split()[0] for line in output.splitlines() if line.strip()} + missing = expected - exported + leaked = exported - expected - set(args.allow_export) + if missing or leaked: + if missing: + print("Missing public exports:", ", ".join(sorted(missing))) + if leaked: + print("Unexpected exports:", ", ".join(sorted(leaked))) + raise SystemExit(1) + print(f"Verified {len(expected)} public exports") + + +if __name__ == "__main__": + main() diff --git a/tests/helpers/run_backends.py b/tests/helpers/run_backends.py index 9ad9b0d..8c5428a 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 = sys.argv[1:] + weston, xwayland, test, timers = sys.argv[1:] servers = [] with tempfile.TemporaryDirectory(prefix="yasl-backends-") as directory: root = Path(directory) @@ -73,6 +73,13 @@ def main(): run("failure", 2, DISPLAY=":59999") run("failure", 0, WAYLAND_DISPLAY="missing-socket", DISPLAY=":59999") run("mixed") + print("Test: Wayland surface timers", flush=True) + subprocess.run([timers], env=env, check=True, timeout=8) + x_env = env.copy() + x_env.pop("WAYLAND_DISPLAY", None) + x_env["XDG_SESSION_TYPE"] = "x11" + print("Test: X11 surface timers", flush=True) + subprocess.run([timers], env=x_env, check=True, timeout=8) except BaseException: for log in (wl_log, x_log): log.flush() diff --git a/tests/meson.build b/tests/meson.build index f2d5e79..0f28c39 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -12,6 +12,47 @@ add_test_setup('msan') test('basic', test_exe) +shared_dep = declare_dependency( + link_with: yasl_lib.get_shared_lib(), + include_directories: include_directories('../include'), +) + +shared_api_test = executable( + 'yasl-shared-api-test', + 'test_lifecycle.c', + dependencies: shared_dep, +) +test('shared-api', shared_api_test) + +if add_languages('cpp', native: false, required: false) + cpp = meson.get_compiler('cpp') + cpp_api_test = executable( + 'yasl-cpp-api-test', + 'test_api.cpp', + dependencies: shared_dep, + cpp_args: cpp.get_supported_arguments('-pedantic-errors'), + override_options: ['cpp_std=c++11'], + ) + test('cpp-api', cpp_api_test) +endif + +python = find_program('python3') +if system == 'linux' + nm = find_program('nm') + export_args = [] + if get_option('b_sanitize').contains('address') + # Clang's ASan instrumentation exports these linker-generated markers. + export_args += ['--allow-export=__start_asan_globals', + '--allow-export=__stop_asan_globals'] + endif + test('public-exports', python, + args: [files('helpers/check_exports.py')] + export_args + + [nm, yasl_lib.get_shared_lib(), + files('../include/yasl/yasl.h', '../include/yasl/timing.h', + '../include/yasl/loop.h', '../include/yasl/native.h')], + ) +endif + contracts_test = executable( 'yasl-contracts-test', 'common/test_contracts.c', @@ -54,11 +95,11 @@ endif if get_option('integration_tests') if has_cocoa native_test = executable('yasl-native-test', 'cocoa/test_native.c', - dependencies: yasl_dep, + dependencies: shared_dep, ) test('native', native_test, timeout: 20) ownership_test = executable('yasl-ownership-test', 'cocoa/test_ownership.m', - dependencies: yasl_dep, + dependencies: [shared_dep, deps], ) test('native-ownership', ownership_test, timeout: 20) else @@ -68,13 +109,13 @@ if get_option('integration_tests') backend_test = executable( 'yasl-backend-test', 'common/test_backends.c', - dependencies: yasl_dep, + dependencies: [shared_dep, deps], ) - python = find_program('python3') weston = find_program('weston') xwayland = find_program('Xwayland') test('backends', python, - args: [files('helpers/run_backends.py'), weston, xwayland, backend_test], + args: [files('helpers/run_backends.py'), weston, xwayland, + backend_test, timer_test], timeout: 60, ) endif diff --git a/tests/test_api.cpp b/tests/test_api.cpp new file mode 100644 index 0000000..70c4ff7 --- /dev/null +++ b/tests/test_api.cpp @@ -0,0 +1,27 @@ +#include "yasl/native.h" +#include "yasl/loop.h" +#include +#include +#include + +int main() +{ + yasl_timing timing = yasl_surface_timing(nullptr); + assert(timing.source == YASL_TIMING_UNKNOWN); + assert(yasl_scale_to_device(10.0, 240) == 20); + yasl_geometry geometry = yasl_surface_geometry(nullptr); + assert(geometry.scale_120 == YASL_SCALE_UNIT); + assert(!yasl_display_get_native_handle(nullptr)); + assert(!yasl_surface_get_native_handle(nullptr)); + yasl_native_frame native{}; + assert(!yasl_surface_get_native_frame(nullptr, nullptr, &native)); + + yasl_loop *loop = yasl_loop_create(); + assert(loop); + assert(yasl_loop_step(loop, 0) >= 0); + yasl_loop_destroy(loop); + + yasl_input_event event{}; + assert(!yasl_surface_input_poll(nullptr, &event)); + return 0; +} diff --git a/tests/test_lifecycle.c b/tests/test_lifecycle.c index 681e5fc..d04662f 100644 --- a/tests/test_lifecycle.c +++ b/tests/test_lifecycle.c @@ -1,3 +1,5 @@ +#include "yasl/loop.h" +#include "yasl/timing.h" #include "yasl/yasl.h" #include #include @@ -11,21 +13,6 @@ static void test_scaling(void) assert(yasl_scale_to_logical(20, 240) == 10); } -static void test_timing_clock(void) -{ - struct yasl_frame_clock clock = { .refresh_ns = 16666666.0 }; - yasl_clock_presented(&clock, 1000000000, true); - assert(clock.last_present_ns == 1000000000); - uint64_t next = yasl_clock_next_present_ns(&clock); - assert(next > 1000000000); - - yasl_clock_note_resize(&clock, 500000); - struct yasl_resize_stats stats; - yasl_clock_resize_stats(&clock, &stats); - assert(stats.count == 1); - assert(stats.max_us == 500.0); -} - static int dummy_cb(int fd, short revents, void *ud) { (void)fd; (void)revents; (void)ud; @@ -68,12 +55,12 @@ static void test_display_lifecycle_standalone(void) assert(s != NULL); assert(yasl_surface_display(s) == d); - struct yasl_vector2i sz = yasl_surface_size(s); + struct yasl_vector2i sz = yasl_surface_geometry(s).size; assert(sz.x > 0 && sz.y > 0); yasl_surface_schedule_redraw(s); yasl_display_flush(d); - yasl_display_dispatch_timeout(d, 0); + yasl_display_dispatch(d, 0); yasl_surface_destroy(s); yasl_display_close(d); @@ -82,7 +69,6 @@ static void test_display_lifecycle_standalone(void) int main(void) { test_scaling(); - test_timing_clock(); test_loop(); test_display_lifecycle_standalone(); printf("All YASL basic tests passed.\n"); diff --git a/tests/wayland/test_input.c b/tests/wayland/test_input.c index f421e6c..7b91a9d 100644 --- a/tests/wayland/test_input.c +++ b/tests/wayland/test_input.c @@ -1,8 +1,33 @@ #include "wayland/internal.h" #include +static void test_popup_bounds(void) +{ + struct seat seat = {.input_serial = 1}; + struct wayland_display d = {.base.ops = &yasl_wayland_backend, .seats = &seat}; + struct wayland_surface w = {.base.display = &d.base, .conn = &d}; + w.parent = &w; + w.content.x = w.content.y = 1; + /* Invalid bounds must be rejected before accessing any protocol object. */ + w.xdg_surface = (struct xdg_surface *)&w; + w.popup = (struct xdg_popup *)&w; + struct yasl_popup_spec specs[] = { + {.size = {100, 100}, .content_offset = {INT_MAX, 0}, .content_size = {1, 0}}, + {.size = {100, 100}, .content_offset = {0, INT_MAX}, .content_size = {0, 1}}, + {.size = {100, 100}, .content_offset = {1, 0}, .content_size = {INT_MAX, 0}}, + {.size = {100, 100}, .content_offset = {0, 1}, .content_size = {0, INT_MAX}}, + {.size = {100, 100}, .anchor = {INT_MIN, 0, 1, 1}}, + {.size = {100, 100}, .anchor = {0, INT_MIN, 1, 1}}, + }; + for (size_t i = 0; i < sizeof(specs) / sizeof(specs[0]); i++) { + assert(!yasl_surface_create_popup(&w.base, &specs[i])); + assert(!yasl_surface_popup_reposition(&w.base, &specs[i])); + } +} + int main(void) { + test_popup_bounds(); struct wayland_surface w = {0}; struct seat seat = {.ime_focus = &w}; wayland_surface_set_callbacks(&w, NULL, NULL); -- cgit v1.2.3