xref: /aosp_15_r20/external/wayland/tests/test-compositor.c (revision 84e872a0dc482bffdb63672969dd03a827d67c73)
1 /*
2  * Copyright (c) 2014 Red Hat, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include <assert.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <sys/socket.h>
35 #include <sys/wait.h>
36 #include <signal.h>
37 
38 #define WL_HIDE_DEPRECATED
39 
40 #include "test-runner.h"
41 #include "test-compositor.h"
42 
43 int client_log_fd = -1;
44 
45 /* --- Protocol --- */
46 struct test_compositor;
47 
48 static const struct wl_message tc_requests[] = {
49 	/* this request serves as a barrier for synchronizing*/
50 	{ "stop_display", "u", NULL },
51 	{ "noop", "", NULL },
52 };
53 
54 static const struct wl_message tc_events[] = {
55 	{ "display_resumed", "", NULL }
56 };
57 
58 const struct wl_interface test_compositor_interface = {
59 	"test", 1,
60 	2, tc_requests,
61 	1, tc_events
62 };
63 
64 struct test_compositor_interface {
65 	void (*stop_display)(struct wl_client *client,
66 			     struct wl_resource *resource,
67 			     uint32_t num);
68 	void (*noop)(struct wl_client *client,
69 			     struct wl_resource *resource);
70 };
71 
72 struct test_compositor_listener {
73 	void (*display_resumed)(void *data, struct test_compositor *tc);
74 
75 };
76 
77 enum {
78 	STOP_DISPLAY = 0,
79 	TEST_NOOP = 1
80 };
81 
82 enum {
83 	DISPLAY_RESUMED = 0
84 };
85 
86 /* Since tests can run parallelly, we need unique socket names
87  * for each test, otherwise the test can fail on wl_display_add_socket. */
88 static const char *
get_socket_name(void)89 get_socket_name(void)
90 {
91 	struct timeval tv;
92 	static char retval[64];
93 
94 	gettimeofday(&tv, NULL);
95 	snprintf(retval, sizeof retval, "wayland-test-%d-%ld%ld",
96 		 getpid(), tv.tv_sec, tv.tv_usec);
97 
98 	return retval;
99 }
100 
101 static void
handle_client_destroy(void * data)102 handle_client_destroy(void *data)
103 {
104 	struct client_info *ci = data;
105 	struct display *d;
106 	siginfo_t status;
107 
108 	d = ci->display;
109 
110 	assert(waitid(P_PID, ci->pid, &status, WEXITED) != -1);
111 
112 	switch (status.si_code) {
113 	case CLD_KILLED:
114 	case CLD_DUMPED:
115 		fprintf(stderr, "Client '%s' was killed by signal %d\n",
116 			ci->name, status.si_status);
117 		ci->kill_code = status.si_status;
118 		break;
119 	case CLD_EXITED:
120 		if (status.si_status != EXIT_SUCCESS)
121 			fprintf(stderr, "Client '%s' exited with code %d\n",
122 				ci->name, status.si_status);
123 
124 		ci->exit_code = status.si_status;
125 		break;
126 	}
127 
128 	++d->clients_terminated_no;
129 	if (d->clients_no == d->clients_terminated_no) {
130 		wl_display_terminate(d->wl_display);
131 	}
132 
133 	/* the clients are not removed from the list, because
134 	 * at the end of the test we check the exit codes of all
135 	 * clients. In the case that the test would go through
136 	 * the clients list manually, zero out the wl_client as a sign
137 	 * that the client is not running anymore */
138 }
139 
140 /**
141  * Check client's state and terminate display when all clients exited
142  */
143 static void
client_destroyed(struct wl_listener * listener,void * data)144 client_destroyed(struct wl_listener *listener, void *data)
145 {
146 	struct client_info *ci;
147 	struct display *d;
148 	struct wl_event_loop *loop;
149 
150 	/* Wait for client in an idle handler to avoid blocking the actual
151 	 * client destruction (fd close etc. */
152 	ci = wl_container_of(listener, ci, destroy_listener);
153 	d = ci->display;
154 	loop = wl_display_get_event_loop(d->wl_display);
155 	wl_event_loop_add_idle(loop, handle_client_destroy, ci);
156 
157 	ci->wl_client = NULL;
158 }
159 
160 static void
client_log_handler(const char * fmt,va_list arg)161 client_log_handler(const char *fmt, va_list arg)
162 {
163 	va_list arg_copy;
164 
165 	va_copy(arg_copy, arg);
166 	vdprintf(client_log_fd, fmt, arg_copy);
167 	va_end(arg_copy);
168 
169 	vfprintf(stderr, fmt, arg);
170 }
171 
172 static void
run_client(void (* client_main)(void * data),void * data,int wayland_sock,int client_pipe,int log_fd)173 run_client(void (*client_main)(void *data), void *data,
174 	   int wayland_sock, int client_pipe, int log_fd)
175 {
176 	char s[8];
177 	int cur_fds;
178 	int can_continue = 0;
179 
180 	/* Wait until display signals that client can continue */
181 	assert(read(client_pipe, &can_continue, sizeof(int)) == sizeof(int));
182 
183 	if (can_continue == 0)
184 		abort(); /* error in parent */
185 
186 	/* for wl_display_connect() */
187 	snprintf(s, sizeof s, "%d", wayland_sock);
188 	setenv("WAYLAND_SOCKET", s, 0);
189 
190 	/* Capture the log to the specified file descriptor. */
191 	client_log_fd = log_fd;
192 	wl_log_set_handler_client(client_log_handler);
193 
194 	cur_fds = count_open_fds();
195 
196 	client_main(data);
197 
198 	/* Clients using wl_display_connect() will end up closing the socket
199 	 * passed in through the WAYLAND_SOCKET environment variable. When
200 	 * doing this, it clears the environment variable, so if it's been
201 	 * unset, then we assume the client consumed the file descriptor and
202 	 * do not count it towards leak checking. */
203 	if (!getenv("WAYLAND_SOCKET"))
204 		cur_fds--;
205 
206 	check_fd_leaks(cur_fds);
207 }
208 
209 static int
create_log_fd(void)210 create_log_fd(void)
211 {
212 	char logname[] = "/tmp/wayland-tests-log-XXXXXX";
213 	int log_fd = mkstemp(logname);
214 
215 	if (log_fd >= 0)
216 		unlink(logname);
217 
218 	return log_fd;
219 }
220 
221 static struct client_info *
display_create_client(struct display * d,void (* client_main)(void * data),void * data,const char * name)222 display_create_client(struct display *d,
223 		      void (*client_main)(void *data),
224 		      void *data,
225 		      const char *name)
226 {
227 	int pipe_cli[2];
228 	int sock_wayl[2];
229 	pid_t pid;
230 	int can_continue = 0;
231 	struct client_info *cl;
232 	int log_fd;
233 
234 	assert(pipe(pipe_cli) == 0 && "Failed creating pipe");
235 	assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_wayl) == 0
236 	       && "Failed creating socket pair");
237 
238 	log_fd = create_log_fd();
239 	assert(log_fd >= 0 && "Failed to create log fd");
240 
241 	pid = fork();
242 	assert(pid != -1 && "Fork failed");
243 
244 	if (pid == 0) {
245 		close(sock_wayl[1]);
246 		close(pipe_cli[1]);
247 
248 		run_client(client_main, data, sock_wayl[0], pipe_cli[0], log_fd);
249 
250 		close(sock_wayl[0]);
251 		close(pipe_cli[0]);
252 		close(log_fd);
253 
254 		exit(0);
255 	}
256 
257 	close(sock_wayl[0]);
258 	close(pipe_cli[0]);
259 
260 	cl = calloc(1, sizeof(struct client_info));
261 	assert(cl && "Out of memory");
262 
263 	wl_list_insert(&d->clients, &cl->link);
264 
265 	cl->display = d;
266 	cl->name = name;
267 	cl->pid = pid;
268 	cl->pipe = pipe_cli[1];
269 	cl->log_fd = log_fd;
270 	cl->destroy_listener.notify = &client_destroyed;
271 
272 	cl->wl_client = wl_client_create(d->wl_display, sock_wayl[1]);
273 	if (!cl->wl_client) {
274 		int ret;
275 
276 		/* abort the client */
277 		ret = write(cl->pipe, &can_continue, sizeof(int));
278 		assert(ret == sizeof(int) && "aborting the client failed");
279 		assert(0 && "Couldn't create wayland client");
280 	}
281 
282 	wl_client_add_destroy_listener(cl->wl_client,
283 				       &cl->destroy_listener);
284 
285 	++d->clients_no;
286 
287 	return cl;
288 }
289 
290 struct client_info *
client_create_with_name(struct display * d,void (* client_main)(void * data),void * data,const char * name)291 client_create_with_name(struct display *d,
292 			void (*client_main)(void *data), void *data,
293 			const char *name)
294 {
295 	int can_continue = 1;
296 	struct client_info *cl = display_create_client(d,
297 						       client_main, data,
298 						       name);
299 
300 	/* let the show begin! */
301 	assert(write(cl->pipe, &can_continue, sizeof(int)) == sizeof(int));
302 
303 	return cl;
304 }
305 
306 /* wfr = waiting for resume */
307 struct wfr {
308 	struct wl_resource *resource;
309 	struct wl_list link;
310 };
311 
312 static void
handle_stop_display(struct wl_client * client,struct wl_resource * resource,uint32_t num)313 handle_stop_display(struct wl_client *client,
314 		    struct wl_resource *resource, uint32_t num)
315 {
316 	struct display *d = wl_resource_get_user_data(resource);
317 	struct wfr *wfr;
318 
319 	assert(d->wfr_num < num
320 	       && "test error: Too many clients sent stop_display request");
321 
322 	++d->wfr_num;
323 
324 	wfr = malloc(sizeof *wfr);
325 	if (!wfr) {
326 		wl_client_post_no_memory(client);
327 		assert(0 && "Out of memory");
328 	}
329 
330 	wfr->resource = resource;
331 	wl_list_insert(&d->waiting_for_resume, &wfr->link);
332 
333 	if (d->wfr_num == num)
334 		wl_display_terminate(d->wl_display);
335 }
336 
337 static void
handle_noop(struct wl_client * client,struct wl_resource * resource)338 handle_noop(struct wl_client *client, struct wl_resource *resource)
339 {
340 	(void)client;
341 	(void)resource;
342 }
343 
344 static const struct test_compositor_interface tc_implementation = {
345 	handle_stop_display,
346 	handle_noop,
347 };
348 
349 static void
tc_bind(struct wl_client * client,void * data,uint32_t ver,uint32_t id)350 tc_bind(struct wl_client *client, void *data,
351 	uint32_t ver, uint32_t id)
352 {
353 	struct wl_resource *res;
354 
355 	res = wl_resource_create(client, &test_compositor_interface, ver, id);
356 	if (!res) {
357 		wl_client_post_no_memory(client);
358 		assert(0 && "Out of memory");
359 	}
360 
361 	wl_resource_set_implementation(res, &tc_implementation, data, NULL);
362 }
363 
364 struct display *
display_create(void)365 display_create(void)
366 {
367 	struct display *d = NULL;
368 	const char *socket_name;
369 	int stat = 0;
370 
371 	d = calloc(1, sizeof *d);
372 	assert(d && "Out of memory");
373 
374 	d->wl_display = wl_display_create();
375 	assert(d->wl_display && "Creating display failed");
376 
377 	/* hope the path won't be longer than 108 ... */
378 	socket_name = get_socket_name();
379 	stat = wl_display_add_socket(d->wl_display, socket_name);
380 	assert(stat == 0 && "Failed adding socket");
381 
382 	wl_list_init(&d->clients);
383 	d->clients_no = d->clients_terminated_no = 0;
384 
385 	wl_list_init(&d->waiting_for_resume);
386 	d->wfr_num = 0;
387 
388 	d->test_global = wl_global_create(d->wl_display,
389 					  &test_compositor_interface,
390 					  1, d, tc_bind);
391 	assert(d->test_global && "Creating test global failed");
392 
393 	return d;
394 }
395 
396 void
display_run(struct display * d)397 display_run(struct display *d)
398 {
399 	assert(d->wfr_num == 0
400 	       && "test error: Have waiting clients. Use display_resume.");
401 	wl_display_run(d->wl_display);
402 }
403 
404 void
display_post_resume_events(struct display * d)405 display_post_resume_events(struct display *d)
406 {
407 	struct wfr *wfr, *next;
408 
409 	assert(d->wfr_num > 0 && "test error: No clients waiting.");
410 
411 	wl_list_for_each_safe(wfr, next, &d->waiting_for_resume, link) {
412 		wl_resource_post_event(wfr->resource, DISPLAY_RESUMED);
413 		wl_list_remove(&wfr->link);
414 		free(wfr);
415 	}
416 
417 	assert(wl_list_empty(&d->waiting_for_resume));
418 	d->wfr_num = 0;
419 }
420 
421 void
display_resume(struct display * d)422 display_resume(struct display *d)
423 {
424 	display_post_resume_events(d);
425 	wl_display_run(d->wl_display);
426 }
427 
428 /* If signum is 0, expect a successful client exit, otherwise
429  * expect the client to have been killed by that signal. */
430 void
display_destroy_expect_signal(struct display * d,int signum)431 display_destroy_expect_signal(struct display *d, int signum)
432 {
433 	struct client_info *cl, *next;
434 	int failed = 0;
435 
436 	assert(d->wfr_num == 0
437 	       && "test error: Didn't you forget to call display_resume?");
438 
439 	wl_list_for_each_safe(cl, next, &d->clients, link) {
440 		assert(cl->wl_client == NULL);
441 
442 		if (signum != 0 && cl->kill_code != signum) {
443 			++failed;
444 			fprintf(stderr,
445 				"Client '%s' failed, expecting signal %d, "
446 				"got %d\n",
447 				cl->name, signum, cl->kill_code);
448 		}
449 		else if (signum == 0 &&
450 			 (cl->kill_code != 0 || cl->exit_code != 0)) {
451 			++failed;
452 			fprintf(stderr, "Client '%s' failed\n", cl->name);
453 		}
454 
455 		close(cl->pipe);
456 		close(cl->log_fd);
457 		free(cl);
458 	}
459 
460 	wl_global_destroy(d->test_global);
461 	wl_display_destroy(d->wl_display);
462 	free(d);
463 
464 	if (failed) {
465 		fprintf(stderr, "%d child(ren) failed\n", failed);
466 		abort();
467 	}
468 }
469 
470 void
display_destroy(struct display * d)471 display_destroy(struct display *d)
472 {
473 	display_destroy_expect_signal(d, 0);
474 }
475 
476 /*
477  * --- Client helper functions ---
478  */
479 static void
handle_display_resumed(void * data,struct test_compositor * tc)480 handle_display_resumed(void *data, struct test_compositor *tc)
481 {
482 	struct client *c = data;
483 
484 	c->display_stopped = 0;
485 }
486 
487 static const struct test_compositor_listener tc_listener = {
488 	handle_display_resumed
489 };
490 
491 static void
registry_handle_globals(void * data,struct wl_registry * registry,uint32_t id,const char * intf,uint32_t ver)492 registry_handle_globals(void *data, struct wl_registry *registry,
493 			uint32_t id, const char *intf, uint32_t ver)
494 {
495 	struct client *c = data;
496 
497 	if (strcmp(intf, "test") != 0)
498 		return;
499 
500 	c->tc = wl_registry_bind(registry, id, &test_compositor_interface, ver);
501 	assert(c->tc && "Failed binding to registry");
502 
503 	wl_proxy_add_listener((struct wl_proxy *) c->tc,
504 			      (void *) &tc_listener, c);
505 }
506 
507 static const struct wl_registry_listener registry_listener =
508 {
509 	registry_handle_globals,
510 	NULL
511 };
512 
client_connect()513 struct client *client_connect()
514 {
515 	struct wl_registry *reg;
516 	struct client *c = calloc(1, sizeof *c);
517 	assert(c && "Out of memory");
518 
519 	c->wl_display = wl_display_connect(NULL);
520 	assert(c->wl_display && "Failed connecting to display");
521 
522 	/* create test_compositor proxy. Do it with temporary
523 	 * registry so that client can define it's own listener later */
524 	reg = wl_display_get_registry(c->wl_display);
525 	assert(reg);
526 	wl_registry_add_listener(reg, &registry_listener, c);
527 	wl_display_roundtrip(c->wl_display);
528 	assert(c->tc);
529 
530 	wl_registry_destroy(reg);
531 
532 	return c;
533 }
534 
535 static void
check_error(struct wl_display * display)536 check_error(struct wl_display *display)
537 {
538 	uint32_t ec, id;
539 	const struct wl_interface *intf;
540 	int err;
541 
542 	err = wl_display_get_error(display);
543 	/* write out message about protocol error */
544 	if (err == EPROTO) {
545 		ec = wl_display_get_protocol_error(display, &intf, &id);
546 		fprintf(stderr, "Client: Got protocol error %u on interface %s"
547 				" (object %u)\n", ec, intf->name, id);
548 	}
549 
550 	if (err) {
551 		fprintf(stderr, "Client error: %s\n", strerror(err));
552 		abort();
553 	}
554 }
555 
556 void
client_disconnect(struct client * c)557 client_disconnect(struct client *c)
558 {
559 	/* check for errors */
560 	check_error(c->wl_display);
561 
562 	wl_proxy_destroy((struct wl_proxy *) c->tc);
563 	wl_display_disconnect(c->wl_display);
564 	free(c);
565 }
566 
567 /* num is number of clients that requests to stop display.
568  * Display is stopped after it receives num STOP_DISPLAY requests */
569 int
stop_display(struct client * c,int num)570 stop_display(struct client *c, int num)
571 {
572 	int n = 0;
573 
574 	c->display_stopped = 1;
575 	wl_proxy_marshal((struct wl_proxy *) c->tc, STOP_DISPLAY, num);
576 
577 	while (c->display_stopped && n >= 0) {
578 		n = wl_display_dispatch(c->wl_display);
579 	}
580 
581 	return n;
582 }
583 
584 void
noop_request(struct client * c)585 noop_request(struct client *c)
586 {
587 	wl_proxy_marshal((struct wl_proxy *) c->tc, TEST_NOOP);
588 }
589