summaryrefslogtreecommitdiff
path: root/tests/common
diff options
context:
space:
mode:
Diffstat (limited to 'tests/common')
-rw-r--r--tests/common/test_backends.c90
-rw-r--r--tests/common/test_contracts.c92
-rw-r--r--tests/common/test_keyboard.c32
-rw-r--r--tests/common/test_utf8.c27
4 files changed, 237 insertions, 4 deletions
diff --git a/tests/common/test_backends.c b/tests/common/test_backends.c
index 4c99646..cf5b1f9 100644
--- a/tests/common/test_backends.c
+++ b/tests/common/test_backends.c
@@ -194,23 +194,105 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
yasl_display_copy(d, YASL_CLIPBOARD, "clipboard text");
yasl_display_paste(d, YASL_CLIPBOARD, w->surface);
pump(loop, d, NULL);
- bool saw_key = false, saw_paste = false;
+ bool saw_key = false, saw_paste = false, saw_text = false;
struct yasl_input_event event;
while (yasl_surface_input_poll(w->surface, &event)) {
assert(event.surface == w->surface);
if (event.kind == YASL_INPUT_KEY) {
assert(event.key.codepoint == 'a');
assert(event.key.shifted == 'A');
- assert(event.key.base == 30);
- saw_key = event.key.key == XK_a;
+ assert(event.key.native_code == key.xkey.keycode);
+ saw_key = event.key.key == YASL_KEY_A;
}
if (event.kind == YASL_INPUT_PASTE) {
assert(event.paste.len == strlen("clipboard text"));
assert(memcmp(event.paste.text, "clipboard text", event.paste.len) == 0);
saw_paste = true;
}
+ if (event.kind == YASL_INPUT_TEXT) {
+ assert(event.text.len == 1 && event.text.text[0] == 'a');
+ saw_text = true;
+ }
+ }
+ assert(saw_key && saw_paste && saw_text);
+ /* Physical identity survives modifier and layout changes, including a
+ * release whose modifiers differ from its press. */
+ key.xkey.state = ShiftMask;
+ XSendEvent(native, window, False, KeyPressMask, &key);
+ key.xkey.type = KeyRelease;
+ key.xkey.state = 0;
+ XSendEvent(native, window, False, KeyReleaseMask, &key);
+ XFlush(native);
+ pump(loop, d, NULL);
+ int key_count = 0, text_count = 0;
+ while (yasl_surface_input_poll(w->surface, &event)) {
+ if (event.kind == YASL_INPUT_KEY) {
+ assert(event.key.key == YASL_KEY_A);
+ assert(event.key.action == (key_count ? YASL_KEY_RELEASE : YASL_KEY_PRESS));
+ key_count++;
+ } else if (event.kind == YASL_INPUT_TEXT) {
+ assert(event.text.len == 1 && event.text.text[0] == 'A');
+ text_count++;
+ }
+ }
+ assert(key_count == 2 && text_count == 1);
+ key.xkey.type = KeyPress;
+ key.xkey.state = ControlMask;
+ XSendEvent(native, window, False, KeyPressMask, &key);
+ XFlush(native);
+ pump(loop, d, NULL);
+ assert(yasl_surface_input_poll(w->surface, &event));
+ assert(event.kind == YASL_INPUT_KEY && event.key.key == YASL_KEY_A);
+ assert(event.key.mods & YASL_MOD_CTRL);
+ assert(!yasl_surface_input_poll(w->surface, &event));
+
+ int symbols_per_key = 0;
+ KeySym *saved_symbols = XGetKeyboardMapping(native, key.xkey.keycode, 1, &symbols_per_key);
+ assert(saved_symbols && symbols_per_key >= 2);
+ KeySym *changed_symbols = calloc((size_t)symbols_per_key, sizeof(KeySym));
+ assert(changed_symbols);
+ changed_symbols[0] = XK_q;
+ changed_symbols[1] = XK_Q;
+ XChangeKeyboardMapping(native, key.xkey.keycode, symbols_per_key, changed_symbols, 1);
+ XSync(native, False);
+ pump(loop, d, NULL); /* Consume MappingNotify before translating the key. */
+ key.xkey.state = 0;
+ XSendEvent(native, window, False, KeyPressMask, &key);
+ XFlush(native);
+ pump(loop, d, NULL);
+ saw_key = saw_text = false;
+ while (yasl_surface_input_poll(w->surface, &event)) {
+ if (event.kind == YASL_INPUT_KEY) {
+ assert(event.key.key == YASL_KEY_A && event.key.codepoint == 'q');
+ saw_key = true;
+ } else if (event.kind == YASL_INPUT_TEXT) {
+ assert(event.text.len == 1 && event.text.text[0] == 'q');
+ saw_text = true;
+ }
+ }
+ assert(saw_key && saw_text);
+ XChangeKeyboardMapping(native, key.xkey.keycode, symbols_per_key, saved_symbols, 1);
+ free(changed_symbols);
+ XFree(saved_symbols);
+ XSync(native, False);
+ pump(loop, d, NULL);
+
+ for (unsigned button = 4; button <= 7; button++) {
+ XEvent wheel = {.xbutton = {.type = ButtonPress, .display = native,
+ .window = window, .root = DefaultRootWindow(native),
+ .same_screen = True, .button = button}};
+ /* Direct delivery exercises the core fallback even with XI2 enabled. */
+ XSendEvent(native, window, False, NoEventMask, &wheel);
+ wheel.xbutton.type = ButtonRelease;
+ XSendEvent(native, window, False, NoEventMask, &wheel);
+ XFlush(native);
+ pump(loop, d, NULL);
+ assert(yasl_surface_input_poll(w->surface, &event));
+ assert(event.kind == YASL_INPUT_MOUSE && event.mouse.kind == YASL_PTR_SCROLL);
+ assert(event.mouse.scroll.x == (button == 6 ? -1 : button == 7 ? 1 : 0));
+ assert(event.mouse.scroll.y == (button == 4 ? -1 : button == 5 ? 1 : 0));
+ assert(!yasl_surface_input_poll(w->surface, &event));
}
- assert(saw_key && saw_paste);
assert(yasl_surface_timing(w->surface).source == YASL_TIMING_SUBMISSION);
assert(!(yasl_display_caps(d) & YASL_CAP_FRAME_PACING));
diff --git a/tests/common/test_contracts.c b/tests/common/test_contracts.c
index 4e22de1..8580dd6 100644
--- a/tests/common/test_contracts.c
+++ b/tests/common/test_contracts.c
@@ -232,6 +232,97 @@ static void test_scale(void)
assert(yasl_scale_to_logical(1, 0) == 0);
}
+static int popup_calls;
+static struct yasl_surface *popup_create(struct yasl_surface *parent,
+ const struct yasl_popup_spec *spec)
+{
+ (void)parent; (void)spec;
+ popup_calls++;
+ return NULL; /* Backend fails without setting errno. */
+}
+
+static bool popup_reposition(struct yasl_surface *popup,
+ const struct yasl_popup_spec *spec)
+{
+ (void)popup; (void)spec;
+ popup_calls++;
+ errno = EBUSY;
+ return false;
+}
+
+static uint32_t popup_caps(const struct yasl_display *d)
+{ (void)d; return YASL_CAP_POPUPS; }
+
+static int dispatch_failure(struct yasl_display *d, int timeout)
+{ (void)d; (void)timeout; return -1; }
+
+static struct yasl_surface *create_failure(struct yasl_display *d,
+ const struct yasl_surface_spec *spec, void *device)
+{ (void)d; (void)spec; (void)device; return NULL; }
+
+static int drag_starts;
+static bool drag_without_listener(struct yasl_display *d, struct yasl_surface *s,
+ const struct yasl_drag_image *image, const struct yasl_drag_listener *listener,
+ void *ud)
+{
+ (void)d; (void)s; (void)image; (void)ud;
+ /* Backends may require a table even when no completion callback is needed. */
+ assert(listener && !listener->end);
+ drag_starts++;
+ return true;
+}
+
+static void test_errors(void)
+{
+ const struct yasl_backend_ops ops = {.display_caps = popup_caps,
+ .surface_create_toplevel = create_failure,
+ .surface_create_popup = popup_create,
+ .surface_popup_reposition = popup_reposition,
+ .display_drag_start = drag_without_listener,
+ .display_dispatch_timeout = dispatch_failure};
+ struct yasl_display d = {.ops = &ops};
+ struct yasl_surface s = {.display = &d};
+ struct yasl_popup_spec spec = {.size = {120, 80}, .anchor = {0, 0, 1, 1}};
+ assert(!yasl_surface_create_popup(NULL, &spec) && errno == EINVAL);
+ assert(!yasl_surface_create_popup(&s, NULL) && errno == EINVAL);
+ spec.anchor.width = -1; /* Must not reach Wayland's protocol marshaler. */
+ assert(!yasl_surface_create_popup(&s, &spec) && errno == EINVAL);
+ assert(!yasl_surface_popup_reposition(&s, &spec) && errno == EINVAL);
+ spec.anchor.width = 1;
+ spec.content_offset.x = INT_MAX;
+ spec.content_size.x = INT_MAX;
+ assert(!yasl_surface_create_popup(&s, &spec) && errno == EINVAL);
+ spec.content_offset.x = spec.content_size.x = 0;
+ spec.size.x = 32768;
+ assert(!yasl_surface_create_popup(&s, &spec) && errno == EINVAL);
+ assert(!popup_calls);
+ spec.size.x = 120;
+ errno = ERANGE;
+ assert(!yasl_surface_create_popup(&s, &spec) && errno == ENODEV);
+ assert(!yasl_surface_popup_reposition(&s, &spec) && errno == EBUSY);
+ assert(popup_calls == 2);
+ const struct yasl_backend_ops unsupported = {0};
+ d.ops = &unsupported;
+ assert(!yasl_surface_create_popup(&s, &spec) && errno == ENOTSUP);
+ assert(!yasl_surface_popup_reposition(&s, &spec) && errno == ENOTSUP);
+ assert(yasl_display_dispatch(&d, 0) == -1 && errno == ENOTSUP);
+ assert(yasl_display_dispatch(NULL, 0) == -1 && errno == EINVAL);
+ d.ops = &ops;
+ errno = ERANGE;
+ assert(yasl_display_dispatch(&d, 0) == -1 && errno == EIO);
+ struct yasl_surface_spec toplevel = {.size = {120, 80}};
+ errno = ERANGE;
+ assert(!yasl_surface_create_toplevel(&d, &toplevel) && errno == ENODEV);
+ assert(yasl_display_drag_start(&d, &s, NULL, NULL, NULL));
+ struct yasl_drag_image image = {.size = {1, 1}};
+ assert(!yasl_display_drag_start(&d, &s, &image, NULL, NULL));
+ uint32_t pixel = 0;
+ image.pixels = &pixel;
+ image.size.x = INT_MAX;
+ assert(!yasl_display_drag_start(&d, &s, &image, NULL, NULL));
+ assert(drag_starts == 1);
+}
+
int main(void)
{
test_timing_clock();
@@ -239,5 +330,6 @@ int main(void)
test_validation();
test_native();
test_scale();
+ test_errors();
return 0;
}
diff --git a/tests/common/test_keyboard.c b/tests/common/test_keyboard.c
new file mode 100644
index 0000000..5a7f18e
--- /dev/null
+++ b/tests/common/test_keyboard.c
@@ -0,0 +1,32 @@
+#include "common/keyboard.h"
+#include "cocoa/keycodes.h"
+#include <assert.h>
+
+int main(void)
+{
+ /* Same physical positions across native encodings, including keypad and
+ * left/right modifiers. macOS A is code zero, which is not UNKNOWN. */
+ assert(yasl_key_from_evdev(30) == YASL_KEY_A);
+ assert(yasl_key_from_cocoa(kVK_ANSI_A) == YASL_KEY_A);
+ assert(yasl_key_from_xkb_name("AC01") == YASL_KEY_A);
+ assert(yasl_key_from_evdev(1) == YASL_KEY_ESCAPE);
+ assert(yasl_key_from_cocoa(kVK_Escape) == YASL_KEY_ESCAPE);
+ assert(yasl_key_from_xkb_name("ESC\0") == YASL_KEY_ESCAPE);
+ assert(yasl_key_from_evdev(96) == YASL_KEY_KP_ENTER);
+ assert(yasl_key_from_cocoa(kVK_ANSI_KeypadEnter) == YASL_KEY_KP_ENTER);
+ assert(yasl_key_from_cocoa(kVK_Return) == YASL_KEY_ENTER);
+ assert(yasl_key_from_evdev(100) == YASL_KEY_RIGHT_ALT);
+ assert(yasl_key_from_cocoa(kVK_RightOption) == YASL_KEY_RIGHT_ALT);
+ assert(yasl_key_from_evdev(42) == YASL_KEY_LEFT_SHIFT);
+ assert(yasl_key_from_cocoa(kVK_Shift) == YASL_KEY_LEFT_SHIFT);
+ assert(yasl_key_from_evdev(86) == YASL_KEY_ISO_BACKSLASH);
+ assert(yasl_key_from_cocoa(kVK_ISO_Section) == YASL_KEY_ISO_BACKSLASH);
+ assert(yasl_key_from_evdev(0) == YASL_KEY_UNKNOWN);
+ assert(yasl_key_from_evdev(UINT32_MAX) == YASL_KEY_UNKNOWN);
+ assert(yasl_key_from_cocoa(UINT32_MAX) == YASL_KEY_UNKNOWN);
+ assert(yasl_key_from_xkb_name("ZZZZ") == YASL_KEY_UNKNOWN);
+ assert(yasl_key_label(0x1f600) == 0x1f600);
+ assert(yasl_key_label(0xd800) == 0 && yasl_key_label(0x110000) == 0);
+ assert(yasl_key_label(0xf700) == 0 && yasl_key_label('\t') == 0);
+ return 0;
+}
diff --git a/tests/common/test_utf8.c b/tests/common/test_utf8.c
new file mode 100644
index 0000000..72f8904
--- /dev/null
+++ b/tests/common/test_utf8.c
@@ -0,0 +1,27 @@
+#include "common/utf8.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(void)
+{
+ char latin1[256];
+ for (int i = 1; i <= 255; i++) latin1[i - 1] = (char)i;
+ latin1[255] = '\0';
+ size_t len = 0, back_len = 0;
+ char *utf8 = yasl_utf8_from_latin1(latin1, 255, &len);
+ assert(utf8 && yasl_utf8_validate(utf8, len));
+ char *back = yasl_utf8_to_latin1(utf8, &back_len);
+ assert(back && back_len == 255 && memcmp(back, latin1, 256) == 0);
+ free(back);
+ free(utf8);
+ assert(!yasl_utf8_to_latin1("😀", &len));
+ assert(!yasl_utf8_to_latin1("\xc3", &len));
+ assert(!yasl_utf8_validate("\xc3", 1));
+ assert(!yasl_utf8_validate("\xe2\x82", 2));
+ assert(!yasl_utf8_validate("\xf0\x9f\x98", 3));
+ assert(!yasl_utf8_validate("\xed\xa0\x80", 3)); /* Surrogate. */
+ assert(yasl_utf8_validate("😀", sizeof("😀") - 1));
+ assert(yasl_utf8_validate(NULL, 0));
+ return 0;
+}