aboutsummaryrefslogtreecommitdiff
path: root/tests/display-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/display-test.c')
-rw-r--r--tests/display-test.c276
1 files changed, 275 insertions, 1 deletions
diff --git a/tests/display-test.c b/tests/display-test.c
index 6d98cc7..3db7c95 100644
--- a/tests/display-test.c
+++ b/tests/display-test.c
@@ -60,7 +60,7 @@ display_destroy_notify(struct wl_listener *l, void *data)
{
struct display_destroy_listener *listener;
- listener = container_of(l, struct display_destroy_listener, listener);
+ listener = wl_container_of(l, listener, listener);
listener->done = 1;
}
@@ -1355,3 +1355,277 @@ TEST(zombie_fd_errant_consumption)
display_destroy(d);
}
+
+
+static void
+registry_bind_interface_mismatch_handle_global(void *data,
+ struct wl_registry *registry,
+ uint32_t id, const char *intf,
+ uint32_t ver)
+{
+ uint32_t *seat_id_ptr = data;
+
+ if (strcmp(intf, wl_seat_interface.name) == 0) {
+ *seat_id_ptr = id;
+ }
+}
+
+static const struct wl_registry_listener bind_interface_mismatch_registry_listener = {
+ registry_bind_interface_mismatch_handle_global,
+ NULL
+};
+
+static void
+registry_bind_interface_mismatch_client(void *data)
+{
+ struct client *c = client_connect();
+ struct wl_registry *registry;
+ uint32_t seat_id = 0;
+ void *ptr;
+ int ret;
+
+ registry = wl_display_get_registry(c->wl_display);
+ wl_registry_add_listener(registry,
+ &bind_interface_mismatch_registry_listener,
+ &seat_id);
+
+ ret = wl_display_roundtrip(c->wl_display);
+ assert(ret >= 0);
+ assert(seat_id != 0);
+
+ /* Bind with a different interface */
+ ptr = wl_registry_bind(registry, seat_id, &wl_output_interface, 1);
+ ret = wl_display_roundtrip(c->wl_display);
+ assert(ret < 0);
+ check_bind_error(c);
+
+ wl_proxy_destroy((struct wl_proxy *) ptr);
+ wl_registry_destroy(registry);
+
+ client_disconnect_nocheck(c);
+}
+
+TEST(registry_bind_interface_mismatch)
+{
+ struct display *d;
+ struct wl_global *seat_global;
+
+ d = display_create();
+
+ seat_global = wl_global_create(d->wl_display, &wl_seat_interface,
+ 1, NULL, NULL);
+
+ client_create_noarg(d, registry_bind_interface_mismatch_client);
+ display_run(d);
+
+ wl_global_destroy(seat_global);
+
+ display_destroy(d);
+}
+
+static void
+send_overflow_client(void *data)
+{
+ struct client *c = client_connect();
+ int i, err = 0;
+ int *pipes = data;
+ char tmp = '\0';
+ int sock, optval = 16384;
+
+ /* Limit the send buffer size for the display socket to guarantee
+ * that the test will cause an overflow. */
+ sock = wl_display_get_fd(c->wl_display);
+ assert(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == 0);
+
+ /* Request to break out of 'display_run' in the main process */
+ assert(stop_display(c, 1) >= 0);
+
+ /* On Linux, the actual socket data + metadata space is twice `optval`;
+ * since each noop request requires 8 bytes, the buffer should overflow
+ * within <=4096 iterations. */
+ for (i = 0; i < 1000000; i++) {
+ noop_request(c);
+ err = wl_display_get_error(c->wl_display);
+ if (err)
+ break;
+ }
+
+ /* Do not close the pipe file descriptors afterwards, because the leak
+ * check verifies that the initial/final FD counts are the same */
+ assert(write(pipes[1], &tmp, sizeof(tmp)) == (ssize_t)sizeof(tmp));
+
+ /* Expect an error */
+ fprintf(stderr, "Send loop failed on try %d, err = %d, %s\n", i, err, strerror(err));
+ assert(err == EAGAIN);
+
+ client_disconnect_nocheck(c);
+}
+
+TEST(send_overflow_disconnection)
+{
+ struct display *d;
+ char tmp;
+ int rpipe[2];
+ ssize_t ret;
+
+ assert(pipe(rpipe) != -1);
+
+ d = display_create();
+
+ (void) client_create(d, send_overflow_client, &rpipe);
+
+ /* Close write end of the pipe, so that the later read() call gets
+ * interrupted if the client dies */
+ close(rpipe[1]);
+
+ /* Run the display until the client sends a `stop_display`, then
+ * send a resume message but don't actually look at new messages */
+ display_run(d);
+ display_post_resume_events(d);
+ wl_display_flush_clients(d->wl_display);
+
+ /* Wait until all noop requests have been sent (read returns 1), or
+ * until client process aborts (read returns 0) */
+ do {
+ ret = read(rpipe[0], &tmp, sizeof(tmp));
+ } while (ret == -1 && errno == EINTR);
+ assert(ret != -1);
+ close(rpipe[0]);
+
+ /* For a clean shutdown */
+ display_run(d);
+
+ display_destroy(d);
+}
+
+static void
+registry_global_remove_before_handle_global(void *data,
+ struct wl_registry *registry,
+ uint32_t id, const char *intf,
+ uint32_t ver)
+{
+ uint32_t *id_ptr = data;
+
+ if (strcmp(intf, wl_seat_interface.name) == 0) {
+ assert(*id_ptr == 0);
+ *id_ptr = id;
+ }
+}
+
+static void
+registry_global_remove_before_handle_global_remove(void *data,
+ struct wl_registry *registry,
+ uint32_t id)
+{
+ uint32_t *id_ptr = data;
+
+ if (*id_ptr == id) {
+ *id_ptr = 0;
+ }
+}
+
+/* This listener expects a uint32_t user data pointer, sets it to the wl_seat
+ * global ID when receiving a "global" event, and sets it to zero when receiving
+ * a "global_remove" event. */
+static const struct wl_registry_listener global_remove_before_registry_listener = {
+ registry_global_remove_before_handle_global,
+ registry_global_remove_before_handle_global_remove,
+};
+
+static void
+global_remove_before_client(void *data)
+{
+ struct client *c = client_connect();
+ struct wl_registry *registry;
+ uint32_t global_id = 0, saved_global_id;
+ struct wl_seat *seat;
+ int ret;
+
+ registry = wl_display_get_registry(c->wl_display);
+ wl_registry_add_listener(registry,
+ &global_remove_before_registry_listener,
+ &global_id);
+
+ ret = wl_display_roundtrip(c->wl_display);
+ assert(ret >= 0);
+ assert(global_id != 0);
+ saved_global_id = global_id;
+
+ /* Wait for the compositor to remove the global */
+ assert(stop_display(c, 1) >= 0);
+
+ /* Check binding still works after the global has been removed. Also
+ * check we get the global_remove event. */
+ seat = wl_registry_bind(registry, saved_global_id, &wl_seat_interface, 1);
+ ret = wl_display_roundtrip(c->wl_display);
+ assert(ret >= 0);
+ assert(global_id == 0);
+
+ wl_seat_destroy(seat);
+ wl_registry_destroy(registry);
+
+ client_disconnect(c);
+}
+
+static void
+registry_global_remove_after_handle_global(void *data,
+ struct wl_registry *registry,
+ uint32_t id, const char *intf,
+ uint32_t ver)
+{
+ /* Make sure the global isn't advertised anymore after being removed */
+ assert(strcmp(intf, wl_seat_interface.name) != 0);
+}
+
+static const struct wl_registry_listener global_remove_after_registry_listener = {
+ registry_global_remove_after_handle_global,
+ NULL,
+};
+
+static void
+global_remove_after_client(void *data)
+{
+ struct client *c = client_connect();
+ struct wl_registry *registry;
+ uint32_t global_id = 0;
+ int ret;
+
+ registry = wl_display_get_registry(c->wl_display);
+ wl_registry_add_listener(registry,
+ &global_remove_after_registry_listener,
+ &global_id);
+
+ ret = wl_display_roundtrip(c->wl_display);
+ assert(ret >= 0);
+
+ wl_registry_destroy(registry);
+
+ client_disconnect(c);
+}
+
+TEST(global_remove)
+{
+ struct display *d;
+ struct wl_global *global;
+
+ d = display_create();
+
+ global = wl_global_create(d->wl_display, &wl_seat_interface,
+ 1, d, bind_seat);
+
+ /* Create a client before removing the global */
+ client_create_noarg(d, global_remove_before_client);
+
+ display_run(d);
+
+ wl_global_remove(global);
+
+ /* Create another client after removing the global */
+ client_create_noarg(d, global_remove_after_client);
+
+ display_resume(d);
+
+ wl_global_destroy(global);
+
+ display_destroy(d);
+}