diff options
Diffstat (limited to 'tests/display-test.c')
-rw-r--r-- | tests/display-test.c | 276 |
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); +} |