summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/common/test_public_api.c131
-rw-r--r--tests/common/test_surface_integration.c77
-rw-r--r--tests/helpers/run_surfaces.py10
-rw-r--r--tests/meson.build16
-rw-r--r--tests/test_header_c11.c11
5 files changed, 240 insertions, 5 deletions
diff --git a/tests/common/test_public_api.c b/tests/common/test_public_api.c
new file mode 100644
index 0000000..a53a8ec
--- /dev/null
+++ b/tests/common/test_public_api.c
@@ -0,0 +1,131 @@
+#include <yasl/yasl.h>
+#include <assert.h>
+#include <string.h>
+
+/* Before yasl_init there is no display: argument errors are reported first, owner-thread
+ * calls fail with ENODEV, and queries return their documented fallbacks. Nothing here may
+ * open a display, so only invalid arguments reach the constructors and openers. */
+int main(void)
+{
+ static char anchor; /* A fake handle; it is compared and never dereferenced. */
+ yasl_surface *fake = (yasl_surface *)&anchor;
+
+ assert(yasl_shutdown() == 0 && yasl_shutdown() == 0);
+ assert(yasl_get_backend() == YASL_BACKEND_AUTO && yasl_get_features() == 0);
+ assert(yasl_get_appearance() == 0 && yasl_get_double_click_ms() == 400);
+
+ /* Invalid constructor arguments fail before any display is opened. */
+ assert(!yasl_surface_create(&(struct yasl_surface_options){.width = 32767, .height = 32767}) &&
+ errno == EINVAL);
+ assert(!yasl_surface_create(&(struct yasl_surface_options){.width = 32768, .height = 1}) &&
+ errno == EINVAL);
+ assert(!yasl_surface_create(&(struct yasl_surface_options){
+ .width = 8, .height = 8, .type = YASL_SURFACE_GL, .gl = {.share = fake}}) &&
+ errno == EINVAL);
+
+ /* Owner-thread calls need a display, whatever the surface argument is. */
+ assert(yasl_surface_destroy(NULL) == 0 && yasl_surface_destroy(fake) == -ENODEV);
+ assert(yasl_surface_set_userdata(NULL, &anchor) == -ENODEV);
+ assert(yasl_surface_set_title(NULL, "x") == -ENODEV);
+ assert(yasl_surface_set_title(fake, NULL) == -ENODEV);
+ assert(yasl_surface_request(NULL, YASL_REQUEST_MAXIMIZE) == -ENODEV);
+ assert(yasl_surface_request(fake, (enum yasl_request)9) == -ENODEV);
+ assert(yasl_surface_request_size(NULL, 10, 10) == -ENODEV);
+ assert(yasl_surface_set_cursor(NULL, YASL_CURSOR_ARROW) == -ENODEV);
+ assert(yasl_surface_set_text_input(NULL, NULL) == -ENODEV);
+ assert(yasl_surface_begin_move_resize(NULL, 0) == -ENODEV);
+ assert(yasl_surface_show_window_menu(NULL, 1, 1) == -ENODEV);
+ assert(yasl_surface_bell(NULL) == -ENODEV);
+ assert(yasl_surface_place_under_pointer(NULL, 1, 1) == -ENODEV);
+ assert(yasl_drag_carry(NULL, 1, 1) == -ENODEV);
+ assert(yasl_drag_start(NULL, NULL) == -ENODEV);
+ assert(yasl_drag_cancel() == -ENODEV);
+ assert(yasl_clipboard_set(YASL_CLIPBOARD, "x") == -ENODEV);
+ assert(yasl_clipboard_set(YASL_PRIMARY, NULL) == -ENODEV);
+ assert(yasl_clipboard_request(NULL, YASL_CLIPBOARD) == -ENODEV);
+ assert(yasl_notify("title", NULL) == -ENODEV);
+ assert(yasl_cpu_present(NULL) == -ENODEV);
+ assert(yasl_gl_set_swap_interval(NULL, 1) == -ENODEV);
+ assert(yasl_gl_present(NULL) == -ENODEV);
+ assert(yasl_gl_make_current(fake) == -ENODEV);
+ assert(!yasl_gl_get_proc_address("glClear") && errno == ENODEV);
+ assert(!yasl_gl_get_proc_address(NULL) && errno == ENODEV);
+ assert(!yasl_metal_get_layer(NULL) && errno == ENODEV);
+ struct yasl_popup_options popup = {.width = 4, .height = 4};
+ assert(yasl_popup_reposition(NULL, &popup) == -ENODEV);
+ assert(!yasl_popup_create(NULL, &popup) && errno == ENODEV);
+
+ /* Queries name their fallbacks. */
+ assert(yasl_surface_get_userdata(NULL) == NULL && yasl_surface_get_userdata(fake) == NULL);
+ struct yasl_surface_info info = yasl_surface_get_info(NULL);
+ assert(info.width == 0 && info.height == 0 && info.pixel_width == 0 && info.pixel_height == 0 &&
+ info.scale == 1.0 && info.state == 0);
+ info = yasl_surface_get_info(fake);
+ assert(info.width == 0 && info.pixel_height == 0 && info.scale == 1.0);
+ int w = 7, h = 7;
+ assert(!yasl_cpu_pixels(NULL, &w, &h) && errno == ENODEV && w == 0 && h == 0);
+ assert(!yasl_cpu_pixels(NULL, NULL, NULL) && errno == ENODEV);
+ struct yasl_native native = {.x11_window = 7};
+ assert(yasl_surface_get_native(NULL, NULL) == -EINVAL);
+ assert(yasl_surface_get_native(NULL, &native) == -ENODEV && native.x11_window == 0);
+
+ /* Waiting needs a display; yasl_wake with no display does nothing and does not crash. */
+ yasl_event e = {.type = YASL_EVENT_QUIT};
+ assert(yasl_wait_event(&e, 0) == -ENODEV && e.type == YASL_EVENT_NONE);
+ yasl_wake();
+ yasl_wake();
+ assert(yasl_shutdown() == 0);
+
+ /* The event clock never runs backwards. */
+ uint64_t last = yasl_time_ns();
+ assert(last > 0);
+ for (int i = 0; i < 1000; i++) {
+ uint64_t now = yasl_time_ns();
+ assert(now >= last);
+ last = now;
+ }
+
+ /* yasl_open_uri rejects bad URIs without opening anything. */
+ assert(yasl_open_uri(NULL) == -EINVAL);
+ assert(yasl_open_uri("") == -EINVAL);
+ assert(yasl_open_uri("no-colon") == -EINVAL);
+ assert(yasl_open_uri("1http://x") == -EINVAL);
+ assert(yasl_open_uri("http://x\n") == -EINVAL);
+
+ /* yasl_drop_next_path walks a text/uri-list: local file lines only, comments skipped. */
+ static const char uris[] =
+ "file:///a/b\r\nhttp://x/y\r\n# comment\r\nfile://localhost/c%20d\r\n";
+ const char *cursor = uris;
+ char out[64] = "keep";
+ assert(yasl_drop_next_path(&cursor, NULL, 0) == 4 && cursor == uris);
+ assert(yasl_drop_next_path(&cursor, out, 4) == 4 && cursor == uris && !strcmp(out, "keep"));
+ assert(yasl_drop_next_path(&cursor, out, 5) == 4 && cursor != uris && !strcmp(out, "/a/b"));
+ assert(yasl_drop_next_path(&cursor, out, sizeof out) == 4 && !strcmp(out, "/c d"));
+ assert(yasl_drop_next_path(&cursor, out, sizeof out) == 0);
+ assert(yasl_drop_next_path(NULL, out, sizeof out) == 0);
+ const char *fresh = uris;
+ assert(yasl_drop_next_path(&fresh, NULL, 1) == 0 && fresh == uris);
+
+ /* yasl_get_system_string: length first, the NUL only when the capacity exceeds it. */
+ char path[4096], full[4096], locale[64];
+ size_t n = yasl_get_system_string(YASL_EXECUTABLE_PATH, NULL, 0);
+ assert(n > 0 && n < sizeof full);
+ assert(yasl_get_system_string(YASL_EXECUTABLE_PATH, full, sizeof full) == n && strlen(full) == n);
+ memset(path, 'x', sizeof path);
+ assert(yasl_get_system_string(YASL_EXECUTABLE_PATH, path, n) == n && path[n - 1] == 'x' &&
+ path[n] == 'x');
+ assert(yasl_get_system_string(YASL_EXECUTABLE_PATH, path, n - 1) == n && path[n - 2] == 'x' &&
+ path[n] == 'x');
+ assert(yasl_get_system_string(YASL_EXECUTABLE_PATH, path, n + 1) == n && path[n] == '\0' &&
+ !strcmp(path, full));
+ size_t ln = yasl_get_system_string(YASL_LOCALE, locale, sizeof locale);
+ assert(ln > 0 && ln < sizeof locale && strlen(locale) == ln && !strpbrk(locale, ".@"));
+ assert(yasl_get_system_string(YASL_LOCALE, NULL, 16) == 0 && errno == EINVAL);
+ assert(yasl_get_system_string((enum yasl_system_string)9, locale, sizeof locale) == 0 &&
+ errno == EINVAL);
+#if defined(__linux__)
+ errno = 0;
+ assert(yasl_get_system_string(YASL_RESOURCE_DIR, path, sizeof path) == 0 && errno == ENOTSUP);
+#endif
+ return 0;
+}
diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c
index 072a307..9067640 100644
--- a/tests/common/test_surface_integration.c
+++ b/tests/common/test_surface_integration.c
@@ -40,6 +40,14 @@ static void push(yasl_surface *s, struct yasl_private_event event)
assert(yasl_private_events_push(s->display, s, &event));
}
static int refreshes;
+/* Whether the display still lists the surface with this address; compared, never dereferenced. */
+static bool listed(struct yasl_private_display *d, uintptr_t id)
+{
+ for (yasl_surface *p = d->surfaces; p; p = p->event_next)
+ if ((uintptr_t)p == id)
+ return true;
+ return false;
+}
static void refresh(yasl_surface *s)
{
refreshes++;
@@ -244,7 +252,74 @@ int main(int argc, char **argv)
s->display->modal = false;
#endif
s->refresh = NULL;
- yasl_surface_destroy(s);
+
+ /* Title and window requests: each either works or reports ENOTSUP. */
+ assert(yasl_surface_set_title(s, "CPU contract renamed") == 0);
+ static const enum yasl_request requests[] = {YASL_REQUEST_RESTORE, YASL_REQUEST_FULLSCREEN,
+ YASL_REQUEST_MAXIMIZE, YASL_REQUEST_MINIMIZE};
+ for (size_t i = 0; i < sizeof requests / sizeof requests[0]; i++) {
+ int r = yasl_surface_request(s, requests[i]);
+ assert(r == 0 || r == -ENOTSUP);
+ }
+ drain();
+ int restored = yasl_surface_request(s, YASL_REQUEST_RESTORE);
+ assert(restored == 0 || restored == -ENOTSUP);
+ drain();
+ /* ENOTSUP is the documented answer when the compositor lacks xdg-system-bell; Wayland
+ * currently reports EIO there, so only the 0-or-errno rule is checked. */
+ assert(yasl_surface_bell(s) <= 0);
+
+ /* Desktop queries and the drag cancel, which is a no-op without a drag. */
+ assert(yasl_get_appearance() <= 7);
+ int double_click = yasl_get_double_click_ms();
+ assert(double_click >= 1 && double_click <= 10000);
+ assert(yasl_drag_cancel() == 0);
+
+ /* Without a pointer press these may fail with an errno; they must simply return. */
+ assert(yasl_surface_begin_move_resize(s, 0) <= 0);
+ assert(yasl_surface_show_window_menu(s, 1, 1) <= 0);
+ assert(yasl_drag_start(s, NULL) <= 0);
+ drain();
+
+ /* A popup needs an input grab to open; if it opens, its parent owns it. */
+ struct yasl_private_display *display = s->display;
+ struct yasl_popup_options popup_options = {
+ .width = 40, .height = 30, .anchor_width = 1, .anchor_height = 1};
+ errno = 0;
+ yasl_surface *popup = yasl_popup_create(s, &popup_options);
+ uintptr_t popup_id = (uintptr_t)popup;
+ if (!popup) {
+ assert(errno != 0);
+ } else {
+ assert(listed(display, popup_id));
+ assert(yasl_popup_reposition(popup, &popup_options) <= 0);
+ int pw = 0, ph = 0;
+ assert(yasl_cpu_pixels(popup, &pw, &ph) && pw > 0 && ph > 0);
+ }
+
+ /* Clipboard: offering text may be refused without input; if accepted, it comes back. */
+ int offered = yasl_clipboard_set(YASL_CLIPBOARD, "yasl");
+ assert(offered <= 0);
+ if (offered == 0) {
+ assert(yasl_clipboard_request(s, YASL_CLIPBOARD) == 0);
+ bool got = false;
+ uint64_t clipboard_deadline = yasl_time_ns() + 2000000000ull;
+ while (!got && yasl_time_ns() < clipboard_deadline) {
+ assert(yasl_wait_event(&e, 50) >= 0);
+ if (e.type != YASL_EVENT_CLIPBOARD)
+ continue;
+ got = true;
+ assert(e.surface == s && e.action <= YASL_TRANSFER_FAILED);
+ if (e.action == YASL_TRANSFER_OK)
+ assert(e.length == 4 && !strcmp(e.text, "yasl"));
+ }
+ assert(got);
+ }
+
+ /* Destroying the parent also destroys its popup. */
+ assert(yasl_surface_destroy(s) == 0);
+ if (popup)
+ assert(!listed(display, popup_id));
assert(yasl_init(backend, NULL) == -EALREADY);
s = yasl_surface_create(&(struct yasl_surface_options){.title = "shared backend remains",
.width = 64, .height = 64});
diff --git a/tests/helpers/run_surfaces.py b/tests/helpers/run_surfaces.py
index f120e35..c266b3c 100644
--- a/tests/helpers/run_surfaces.py
+++ b/tests/helpers/run_surfaces.py
@@ -18,7 +18,8 @@ def wait_for(process, ready):
raise TimeoutError('Display server readiness')
def main():
- weston, xwayland, contract, hello, tablet, gl, vulkan, notepad = sys.argv[1:]
+ # Optional trailing binaries run only in the X11 phase.
+ weston, xwayland, contract, hello, tablet, gl, vulkan, notepad, *x11_only = sys.argv[1:]
servers = []
with tempfile.TemporaryDirectory(prefix='yasl-surfaces-') as directory:
root = Path(directory)
@@ -49,9 +50,12 @@ def main():
if backend == 'x11':
case.pop('WAYLAND_DISPLAY', None)
case['XDG_SESSION_TYPE'] = 'x11'
- for binary, args in ((contract,[backend]), (gl,[backend]),
+ cases = [(contract,[backend]), (gl,[backend]),
(vulkan,[backend]), (tablet,['--backend',backend,'--smoke-test']),
- (notepad,['--smoke-test'])):
+ (notepad,['--smoke-test'])]
+ if backend == 'x11':
+ cases += [(binary, []) for binary in x11_only]
+ for binary, args in cases:
if binary == '-':
continue
print('Testing', Path(binary).name, backend, flush=True)
diff --git a/tests/meson.build b/tests/meson.build
index 7e1940e..4a0f492 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -6,6 +6,14 @@ foreach kind : ['static', 'shared']
public_test = executable('yasl-' + kind + '-api-test', 'test_lifecycle.c', dependencies: dep)
test(kind + '-api', public_test)
endforeach
+pre_init_test = executable('yasl-public-api-test', 'common/test_public_api.c', dependencies: yasl_dep)
+test('public-api', pre_init_test)
+foreach std : ['c11', 'c17']
+ header_test = executable('yasl-header-' + std + '-test', 'test_header_c11.c',
+ include_directories: include_directories('../include'),
+ c_args: ['-pedantic-errors'], override_options: ['c_std=' + std])
+ test('header-' + std, header_test)
+endforeach
if add_languages('cpp', native: false, required: false)
cpp_api = executable('yasl-cpp-api-test', 'test_api.cpp', dependencies: shared_dep,
cpp_args: ['-pedantic-errors',
@@ -76,6 +84,11 @@ if has_x11
test('tablet-x11-native', python, args: [files('helpers/run_x11_tablet.py'), xorg, native_tablet], timeout: 30)
endif
endif
+x11_input_args = []
+if has_x11 and get_option('integration_tests')
+ x11_input_args += [executable('yasl-input-x11-test', 'x11/test_input.c',
+ include_directories: inc, dependencies: yasl_dep)]
+endif
if get_option('integration_tests')
display_test_sources = has_cocoa ? files('cocoa/display.m') : []
integration = executable('yasl-surface-integration', ['common/test_surface_integration.c'] + display_test_sources,
@@ -93,7 +106,8 @@ if get_option('integration_tests')
weston = find_program('weston')
xwayland = find_program('Xwayland')
test('surface-backends', python, args: [files('helpers/run_surfaces.py'), weston, xwayland,
- integration, hello_example, tablet_example, gl_integration, vulkan_integration, notepad_example], timeout: 120)
+ integration, hello_example, tablet_example, gl_integration, vulkan_integration, notepad_example] +
+ x11_input_args, timeout: 120)
if has_wayland and has_egl
reconnect = executable('yasl-reconnect-test', 'wayland/test_reconnect.c', dependencies: yasl_dep,
c_args: ['-DTEST_VULKAN=' + (has_vulkan ? '1' : '0')])
diff --git a/tests/test_header_c11.c b/tests/test_header_c11.c
new file mode 100644
index 0000000..515a914
--- /dev/null
+++ b/tests/test_header_c11.c
@@ -0,0 +1,11 @@
+/* Built as strict C11 and C17 with -pedantic-errors: the public header must not need newer C. */
+#include <yasl/yasl.h>
+#include <assert.h>
+
+int main(void)
+{
+ enum yasl_backend backend = YASL_BACKEND_AUTO;
+ struct yasl_surface_options options = {.title = "portable", .width = 64, .height = 64};
+ assert(backend == 0 && options.type == YASL_SURFACE_CPU && YASL_VERSION_MAJOR == 0);
+ return 0;
+}