| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- /*
- * Copyright (c) 2014 Red Hat, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- #include <assert.h>
- #include <errno.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <sys/wait.h>
- #include <signal.h>
- #define WL_HIDE_DEPRECATED
- #include "test-runner.h"
- #include "test-compositor.h"
- int client_log_fd = -1;
- /* --- Protocol --- */
- struct test_compositor;
- static const struct wl_message tc_requests[] = {
- /* this request serves as a barrier for synchronizing*/
- { "stop_display", "u", NULL },
- { "noop", "", NULL },
- };
- static const struct wl_message tc_events[] = {
- { "display_resumed", "", NULL }
- };
- const struct wl_interface test_compositor_interface = {
- "test", 1,
- 2, tc_requests,
- 1, tc_events
- };
- struct test_compositor_interface {
- void (*stop_display)(struct wl_client *client,
- struct wl_resource *resource,
- uint32_t num);
- void (*noop)(struct wl_client *client,
- struct wl_resource *resource);
- };
- struct test_compositor_listener {
- void (*display_resumed)(void *data, struct test_compositor *tc);
- };
- enum {
- STOP_DISPLAY = 0,
- TEST_NOOP = 1
- };
- enum {
- DISPLAY_RESUMED = 0
- };
- /* Since tests can run parallelly, we need unique socket names
- * for each test, otherwise the test can fail on wl_display_add_socket. */
- static const char *
- get_socket_name(void)
- {
- struct timeval tv;
- static char retval[64];
- gettimeofday(&tv, NULL);
- snprintf(retval, sizeof retval, "wayland-test-%d-%lld%lld",
- (int) getpid(), (long long int) tv.tv_sec, (long long int) tv.tv_usec);
- return retval;
- }
- static void
- handle_client_destroy(void *data)
- {
- struct client_info *ci = data;
- struct display *d;
- int status;
- d = ci->display;
- assert(waitpid(ci->pid, &status, 0) != -1);
- if (WIFSIGNALED(status)) {
- fprintf(stderr, "Client '%s' was killed by signal %d\n",
- ci->name, WTERMSIG(status));
- ci->kill_code = WTERMSIG(status);
- } else if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != EXIT_SUCCESS)
- fprintf(stderr, "Client '%s' exited with code %d\n",
- ci->name, WEXITSTATUS(status));
- ci->exit_code = WEXITSTATUS(status);
- }
- ++d->clients_terminated_no;
- if (d->clients_no == d->clients_terminated_no) {
- wl_display_terminate(d->wl_display);
- }
- /* the clients are not removed from the list, because
- * at the end of the test we check the exit codes of all
- * clients. In the case that the test would go through
- * the clients list manually, zero out the wl_client as a sign
- * that the client is not running anymore */
- }
- /**
- * Check client's state and terminate display when all clients exited
- */
- static void
- client_destroyed(struct wl_listener *listener, void *data)
- {
- struct client_info *ci;
- struct display *d;
- struct wl_event_loop *loop;
- /* Wait for client in an idle handler to avoid blocking the actual
- * client destruction (fd close etc. */
- ci = wl_container_of(listener, ci, destroy_listener);
- d = ci->display;
- loop = wl_display_get_event_loop(d->wl_display);
- wl_event_loop_add_idle(loop, handle_client_destroy, ci);
- ci->wl_client = NULL;
- }
- static void
- client_log_handler(const char *fmt, va_list arg)
- {
- va_list arg_copy;
- va_copy(arg_copy, arg);
- vdprintf(client_log_fd, fmt, arg_copy);
- va_end(arg_copy);
- vfprintf(stderr, fmt, arg);
- }
- static void
- run_client(void (*client_main)(void *data), void *data,
- int wayland_sock, int client_pipe, int log_fd)
- {
- char s[8];
- int cur_fds;
- int can_continue = 0;
- /* Wait until display signals that client can continue */
- assert(read(client_pipe, &can_continue, sizeof(int)) == sizeof(int));
- if (can_continue == 0)
- abort(); /* error in parent */
- /* for wl_display_connect() */
- snprintf(s, sizeof s, "%d", wayland_sock);
- setenv("WAYLAND_SOCKET", s, 0);
- /* Capture the log to the specified file descriptor. */
- client_log_fd = log_fd;
- wl_log_set_handler_client(client_log_handler);
- cur_fds = count_open_fds();
- client_main(data);
- /* Clients using wl_display_connect() will end up closing the socket
- * passed in through the WAYLAND_SOCKET environment variable. When
- * doing this, it clears the environment variable, so if it's been
- * unset, then we assume the client consumed the file descriptor and
- * do not count it towards leak checking. */
- if (!getenv("WAYLAND_SOCKET"))
- cur_fds--;
- check_fd_leaks(cur_fds);
- }
- static int
- create_log_fd(void)
- {
- char logname[] = "/tmp/wayland-tests-log-XXXXXX";
- int log_fd = mkstemp(logname);
- if (log_fd >= 0)
- unlink(logname);
- return log_fd;
- }
- static struct client_info *
- display_create_client(struct display *d,
- void (*client_main)(void *data),
- void *data,
- const char *name)
- {
- int pipe_cli[2];
- int sock_wayl[2];
- pid_t pid;
- int can_continue = 0;
- struct client_info *cl;
- int log_fd;
- assert(pipe(pipe_cli) == 0 && "Failed creating pipe");
- assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_wayl) == 0
- && "Failed creating socket pair");
- log_fd = create_log_fd();
- assert(log_fd >= 0 && "Failed to create log fd");
- pid = fork();
- assert(pid != -1 && "Fork failed");
- if (pid == 0) {
- close(sock_wayl[1]);
- close(pipe_cli[1]);
- run_client(client_main, data, sock_wayl[0], pipe_cli[0], log_fd);
- close(sock_wayl[0]);
- close(pipe_cli[0]);
- close(log_fd);
- exit(0);
- }
- close(sock_wayl[0]);
- close(pipe_cli[0]);
- cl = calloc(1, sizeof(struct client_info));
- assert(cl && "Out of memory");
- wl_list_insert(&d->clients, &cl->link);
- cl->display = d;
- cl->name = name;
- cl->pid = pid;
- cl->pipe = pipe_cli[1];
- cl->log_fd = log_fd;
- cl->destroy_listener.notify = &client_destroyed;
- cl->wl_client = wl_client_create(d->wl_display, sock_wayl[1]);
- if (!cl->wl_client) {
- int ret;
- /* abort the client */
- ret = write(cl->pipe, &can_continue, sizeof(int));
- assert(ret == sizeof(int) && "aborting the client failed");
- assert(0 && "Couldn't create wayland client");
- }
- wl_client_add_destroy_listener(cl->wl_client,
- &cl->destroy_listener);
- ++d->clients_no;
- return cl;
- }
- struct client_info *
- client_create_with_name(struct display *d,
- void (*client_main)(void *data), void *data,
- const char *name)
- {
- int can_continue = 1;
- struct client_info *cl = display_create_client(d,
- client_main, data,
- name);
- /* let the show begin! */
- assert(write(cl->pipe, &can_continue, sizeof(int)) == sizeof(int));
- return cl;
- }
- /* wfr = waiting for resume */
- struct wfr {
- struct wl_resource *resource;
- struct wl_list link;
- };
- static void
- handle_stop_display(struct wl_client *client,
- struct wl_resource *resource, uint32_t num)
- {
- struct display *d = wl_resource_get_user_data(resource);
- struct wfr *wfr;
- assert(d->wfr_num < num
- && "test error: Too many clients sent stop_display request");
- ++d->wfr_num;
- wfr = malloc(sizeof *wfr);
- if (!wfr) {
- wl_client_post_no_memory(client);
- assert(0 && "Out of memory");
- }
- wfr->resource = resource;
- wl_list_insert(&d->waiting_for_resume, &wfr->link);
- if (d->wfr_num == num)
- wl_display_terminate(d->wl_display);
- }
- static void
- handle_noop(struct wl_client *client, struct wl_resource *resource)
- {
- (void)client;
- (void)resource;
- }
- static const struct test_compositor_interface tc_implementation = {
- handle_stop_display,
- handle_noop,
- };
- static void
- tc_bind(struct wl_client *client, void *data,
- uint32_t ver, uint32_t id)
- {
- struct wl_resource *res;
- res = wl_resource_create(client, &test_compositor_interface, ver, id);
- if (!res) {
- wl_client_post_no_memory(client);
- assert(0 && "Out of memory");
- }
- wl_resource_set_implementation(res, &tc_implementation, data, NULL);
- }
- struct display *
- display_create(void)
- {
- struct display *d = NULL;
- const char *socket_name;
- int stat = 0;
- d = calloc(1, sizeof *d);
- assert(d && "Out of memory");
- d->wl_display = wl_display_create();
- assert(d->wl_display && "Creating display failed");
- /* hope the path won't be longer than 108 ... */
- socket_name = get_socket_name();
- stat = wl_display_add_socket(d->wl_display, socket_name);
- assert(stat == 0 && "Failed adding socket");
- wl_list_init(&d->clients);
- d->clients_no = d->clients_terminated_no = 0;
- wl_list_init(&d->waiting_for_resume);
- d->wfr_num = 0;
- d->test_global = wl_global_create(d->wl_display,
- &test_compositor_interface,
- 1, d, tc_bind);
- assert(d->test_global && "Creating test global failed");
- return d;
- }
- void
- display_run(struct display *d)
- {
- assert(d->wfr_num == 0
- && "test error: Have waiting clients. Use display_resume.");
- wl_display_run(d->wl_display);
- }
- void
- display_post_resume_events(struct display *d)
- {
- struct wfr *wfr, *next;
- assert(d->wfr_num > 0 && "test error: No clients waiting.");
- wl_list_for_each_safe(wfr, next, &d->waiting_for_resume, link) {
- wl_resource_post_event(wfr->resource, DISPLAY_RESUMED);
- wl_list_remove(&wfr->link);
- free(wfr);
- }
- assert(wl_list_empty(&d->waiting_for_resume));
- d->wfr_num = 0;
- }
- void
- display_resume(struct display *d)
- {
- display_post_resume_events(d);
- wl_display_run(d->wl_display);
- }
- /* If signum is 0, expect a successful client exit, otherwise
- * expect the client to have been killed by that signal. */
- void
- display_destroy_expect_signal(struct display *d, int signum)
- {
- struct client_info *cl, *next;
- int failed = 0;
- assert(d->wfr_num == 0
- && "test error: Didn't you forget to call display_resume?");
- wl_list_for_each_safe(cl, next, &d->clients, link) {
- assert(cl->wl_client == NULL);
- if (signum != 0 && cl->kill_code != signum) {
- ++failed;
- fprintf(stderr,
- "Client '%s' failed, expecting signal %d, "
- "got %d\n",
- cl->name, signum, cl->kill_code);
- }
- else if (signum == 0 &&
- (cl->kill_code != 0 || cl->exit_code != 0)) {
- ++failed;
- fprintf(stderr, "Client '%s' failed\n", cl->name);
- }
- close(cl->pipe);
- close(cl->log_fd);
- free(cl);
- }
- wl_global_destroy(d->test_global);
- wl_display_destroy(d->wl_display);
- free(d);
- if (failed) {
- fprintf(stderr, "%d child(ren) failed\n", failed);
- abort();
- }
- }
- void
- display_destroy(struct display *d)
- {
- display_destroy_expect_signal(d, 0);
- }
- /*
- * --- Client helper functions ---
- */
- static void
- handle_display_resumed(void *data, struct test_compositor *tc)
- {
- struct client *c = data;
- c->display_stopped = 0;
- }
- static const struct test_compositor_listener tc_listener = {
- handle_display_resumed
- };
- static void
- registry_handle_globals(void *data, struct wl_registry *registry,
- uint32_t id, const char *intf, uint32_t ver)
- {
- struct client *c = data;
- if (strcmp(intf, "test") == 0) {
- c->tc = wl_registry_bind(registry, id, &test_compositor_interface, ver);
- assert(c->tc && "Failed binding to registry");
- wl_proxy_add_listener((struct wl_proxy *) c->tc,
- (void *) &tc_listener, c);
- } else if (strcmp(intf, wl_fixes_interface.name) == 0) {
- c->wl_fixes = wl_registry_bind(registry, id, &wl_fixes_interface, 2);
- }
- }
- static const struct wl_registry_listener registry_listener =
- {
- registry_handle_globals,
- NULL
- };
- struct client *client_connect(void)
- {
- struct wl_registry *reg;
- struct client *c = calloc(1, sizeof *c);
- assert(c && "Out of memory");
- c->wl_display = wl_display_connect(NULL);
- assert(c->wl_display && "Failed connecting to display");
- /* create test_compositor proxy. Do it with temporary
- * registry so that client can define its own listener later */
- reg = wl_display_get_registry(c->wl_display);
- assert(reg);
- wl_registry_add_listener(reg, ®istry_listener, c);
- wl_display_roundtrip(c->wl_display);
- assert(c->tc);
- if (c->wl_fixes) {
- wl_fixes_destroy_registry(c->wl_fixes, reg);
- }
- wl_registry_destroy(reg);
- return c;
- }
- static void
- check_error(struct wl_display *display)
- {
- uint32_t ec, id;
- const struct wl_interface *intf;
- int err;
- err = wl_display_get_error(display);
- /* write out message about protocol error */
- if (err == EPROTO) {
- ec = wl_display_get_protocol_error(display, &intf, &id);
- fprintf(stderr, "Client: Got protocol error %u on interface %s"
- " (object %u)\n", ec, intf->name, id);
- }
- if (err) {
- fprintf(stderr, "Client error: %s\n", strerror(err));
- abort();
- }
- }
- void
- client_disconnect(struct client *c)
- {
- /* check for errors */
- check_error(c->wl_display);
- if (c->wl_fixes) {
- wl_fixes_destroy(c->wl_fixes);
- }
- wl_proxy_destroy((struct wl_proxy *) c->tc);
- wl_display_disconnect(c->wl_display);
- free(c);
- }
- /* num is number of clients that requests to stop display.
- * Display is stopped after it receives num STOP_DISPLAY requests */
- int
- stop_display(struct client *c, int num)
- {
- int n = 0;
- c->display_stopped = 1;
- wl_proxy_marshal((struct wl_proxy *) c->tc, STOP_DISPLAY, num);
- while (c->display_stopped && n >= 0) {
- n = wl_display_dispatch(c->wl_display);
- }
- return n;
- }
- void
- noop_request(struct client *c)
- {
- wl_proxy_marshal((struct wl_proxy *) c->tc, TEST_NOOP);
- }
|