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/* --- Protocol --- */
44struct test_compositor;
45
46static const struct wl_message tc_requests[] = {
47 /* this request serves as a barrier for synchronizing*/
48 { "stop_display", "u", NULL },
49 { "noop", "", NULL },
50};
51
52static const struct wl_message tc_events[] = {
53 { "display_resumed", "", NULL }
54};
55
56const struct wl_interface test_compositor_interface = {
57 "test", 1,
58 2, tc_requests,
59 1, tc_events
60};
61
62struct test_compositor_interface {
63 void (*stop_display)(struct wl_client *client,
64 struct wl_resource *resource,
65 uint32_t num);
66 void (*noop)(struct wl_client *client,
67 struct wl_resource *resource);
68};
69
70struct test_compositor_listener {
71 void (*display_resumed)(void *data, struct test_compositor *tc);
72
73};
74
75enum {
76 STOP_DISPLAY = 0,
77 TEST_NOOP = 1
78};
79
80enum {
81 DISPLAY_RESUMED = 0
82};
83
84/* Since tests can run parallelly, we need unique socket names
85 * for each test, otherwise the test can fail on wl_display_add_socket. */
86static const char *
87get_socket_name(void)
88{
89 struct timeval tv;
90 static char retval[64];
91
92 gettimeofday(tv: &tv, NULL);
93 snprintf(s: retval, maxlen: sizeof retval, format: "wayland-test-%d-%ld%ld",
94 getpid(), tv.tv_sec, tv.tv_usec);
95
96 return retval;
97}
98
99static void
100handle_client_destroy(void *data)
101{
102 struct client_info *ci = data;
103 struct display *d;
104 siginfo_t status;
105
106 d = ci->display;
107
108 assert(waitid(P_PID, ci->pid, &status, WEXITED) != -1);
109
110 switch (status.si_code) {
111 case CLD_KILLED:
112 case CLD_DUMPED:
113 fprintf(stderr, format: "Client '%s' was killed by signal %d\n",
114 ci->name, status.si_status);
115 ci->exit_code = status.si_status;
116 break;
117 case CLD_EXITED:
118 if (status.si_status != EXIT_SUCCESS)
119 fprintf(stderr, format: "Client '%s' exited with code %d\n",
120 ci->name, status.si_status);
121
122 ci->exit_code = status.si_status;
123 break;
124 }
125
126 ++d->clients_terminated_no;
127 if (d->clients_no == d->clients_terminated_no) {
128 wl_display_terminate(display: d->wl_display);
129 }
130
131 /* the clients are not removed from the list, because
132 * at the end of the test we check the exit codes of all
133 * clients. In the case that the test would go through
134 * the clients list manually, zero out the wl_client as a sign
135 * that the client is not running anymore */
136}
137
138/**
139 * Check client's state and terminate display when all clients exited
140 */
141static void
142client_destroyed(struct wl_listener *listener, void *data)
143{
144 struct client_info *ci;
145 struct display *d;
146 struct wl_event_loop *loop;
147
148 /* Wait for client in an idle handler to avoid blocking the actual
149 * client destruction (fd close etc. */
150 ci = wl_container_of(listener, ci, destroy_listener);
151 d = ci->display;
152 loop = wl_display_get_event_loop(display: d->wl_display);
153 wl_event_loop_add_idle(loop, func: handle_client_destroy, data: ci);
154
155 ci->wl_client = NULL;
156}
157
158static void
159run_client(void (*client_main)(void *data), void *data,
160 int wayland_sock, int client_pipe)
161{
162 char s[8];
163 int cur_fds;
164 int can_continue = 0;
165
166 /* Wait until display signals that client can continue */
167 assert(read(client_pipe, &can_continue, sizeof(int)) == sizeof(int));
168
169 if (can_continue == 0)
170 abort(); /* error in parent */
171
172 /* for wl_display_connect() */
173 snprintf(s: s, maxlen: sizeof s, format: "%d", wayland_sock);
174 setenv(name: "WAYLAND_SOCKET", value: s, replace: 0);
175
176 cur_fds = count_open_fds();
177
178 client_main(data);
179
180 /* Clients using wl_display_connect() will end up closing the socket
181 * passed in through the WAYLAND_SOCKET environment variable. When
182 * doing this, it clears the environment variable, so if it's been
183 * unset, then we assume the client consumed the file descriptor and
184 * do not count it towards leak checking. */
185 if (!getenv(name: "WAYLAND_SOCKET"))
186 cur_fds--;
187
188 check_fd_leaks(supposed_fds: cur_fds);
189}
190
191static struct client_info *
192display_create_client(struct display *d,
193 void (*client_main)(void *data),
194 void *data,
195 const char *name)
196{
197 int pipe_cli[2];
198 int sock_wayl[2];
199 pid_t pid;
200 int can_continue = 0;
201 struct client_info *cl;
202
203 assert(pipe(pipe_cli) == 0 && "Failed creating pipe");
204 assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_wayl) == 0
205 && "Failed creating socket pair");
206
207 pid = fork();
208 assert(pid != -1 && "Fork failed");
209
210 if (pid == 0) {
211 close(fd: sock_wayl[1]);
212 close(fd: pipe_cli[1]);
213
214 run_client(client_main, data, wayland_sock: sock_wayl[0], client_pipe: pipe_cli[0]);
215
216 close(fd: sock_wayl[0]);
217 close(fd: pipe_cli[0]);
218
219 exit(status: 0);
220 }
221
222 close(fd: sock_wayl[0]);
223 close(fd: pipe_cli[0]);
224
225 cl = calloc(nmemb: 1, size: sizeof(struct client_info));
226 assert(cl && "Out of memory");
227
228 wl_list_insert(list: &d->clients, elm: &cl->link);
229
230 cl->display = d;
231 cl->name = name;
232 cl->pid = pid;
233 cl->pipe = pipe_cli[1];
234 cl->destroy_listener.notify = &client_destroyed;
235
236 cl->wl_client = wl_client_create(display: d->wl_display, fd: sock_wayl[1]);
237 if (!cl->wl_client) {
238 int ret;
239
240 /* abort the client */
241 ret = write(fd: cl->pipe, buf: &can_continue, n: sizeof(int));
242 assert(ret == sizeof(int) && "aborting the client failed");
243 assert(0 && "Couldn't create wayland client");
244 }
245
246 wl_client_add_destroy_listener(client: cl->wl_client,
247 listener: &cl->destroy_listener);
248
249 ++d->clients_no;
250
251 return cl;
252}
253
254struct client_info *
255client_create_with_name(struct display *d,
256 void (*client_main)(void *data), void *data,
257 const char *name)
258{
259 int can_continue = 1;
260 struct client_info *cl = display_create_client(d,
261 client_main, data,
262 name);
263
264 /* let the show begin! */
265 assert(write(cl->pipe, &can_continue, sizeof(int)) == sizeof(int));
266
267 return cl;
268}
269
270/* wfr = waiting for resume */
271struct wfr {
272 struct wl_resource *resource;
273 struct wl_list link;
274};
275
276static void
277handle_stop_display(struct wl_client *client,
278 struct wl_resource *resource, uint32_t num)
279{
280 struct display *d = wl_resource_get_user_data(resource);
281 struct wfr *wfr;
282
283 assert(d->wfr_num < num
284 && "test error: Too many clients sent stop_display request");
285
286 ++d->wfr_num;
287
288 wfr = malloc(size: sizeof *wfr);
289 if (!wfr) {
290 wl_client_post_no_memory(client);
291 assert(0 && "Out of memory");
292 }
293
294 wfr->resource = resource;
295 wl_list_insert(list: &d->waiting_for_resume, elm: &wfr->link);
296
297 if (d->wfr_num == num)
298 wl_display_terminate(display: d->wl_display);
299}
300
301static void
302handle_noop(struct wl_client *client, struct wl_resource *resource)
303{
304 (void)client;
305 (void)resource;
306}
307
308static const struct test_compositor_interface tc_implementation = {
309 handle_stop_display,
310 handle_noop,
311};
312
313static void
314tc_bind(struct wl_client *client, void *data,
315 uint32_t ver, uint32_t id)
316{
317 struct wl_resource *res;
318
319 res = wl_resource_create(client, interface: &test_compositor_interface, version: ver, id);
320 if (!res) {
321 wl_client_post_no_memory(client);
322 assert(0 && "Out of memory");
323 }
324
325 wl_resource_set_implementation(resource: res, implementation: &tc_implementation, data, NULL);
326}
327
328struct display *
329display_create(void)
330{
331 struct display *d = NULL;
332 const char *socket_name;
333 int stat = 0;
334
335 d = calloc(nmemb: 1, size: sizeof *d);
336 assert(d && "Out of memory");
337
338 d->wl_display = wl_display_create();
339 assert(d->wl_display && "Creating display failed");
340
341 /* hope the path won't be longer than 108 ... */
342 socket_name = get_socket_name();
343 stat = wl_display_add_socket(display: d->wl_display, name: socket_name);
344 assert(stat == 0 && "Failed adding socket");
345
346 wl_list_init(list: &d->clients);
347 d->clients_no = d->clients_terminated_no = 0;
348
349 wl_list_init(list: &d->waiting_for_resume);
350 d->wfr_num = 0;
351
352 d->test_global = wl_global_create(display: d->wl_display,
353 interface: &test_compositor_interface,
354 version: 1, data: d, bind: tc_bind);
355 assert(d->test_global && "Creating test global failed");
356
357 return d;
358}
359
360void
361display_run(struct display *d)
362{
363 assert(d->wfr_num == 0
364 && "test error: Have waiting clients. Use display_resume.");
365 wl_display_run(display: d->wl_display);
366}
367
368void
369display_post_resume_events(struct display *d)
370{
371 struct wfr *wfr, *next;
372
373 assert(d->wfr_num > 0 && "test error: No clients waiting.");
374
375 wl_list_for_each_safe(wfr, next, &d->waiting_for_resume, link) {
376 wl_resource_post_event(resource: wfr->resource, opcode: DISPLAY_RESUMED);
377 wl_list_remove(elm: &wfr->link);
378 free(ptr: wfr);
379 }
380
381 assert(wl_list_empty(&d->waiting_for_resume));
382 d->wfr_num = 0;
383}
384
385void
386display_resume(struct display *d)
387{
388 display_post_resume_events(d);
389 wl_display_run(display: d->wl_display);
390}
391
392void
393display_destroy(struct display *d)
394{
395 struct client_info *cl, *next;
396 int failed = 0;
397
398 assert(d->wfr_num == 0
399 && "test error: Didn't you forget to call display_resume?");
400
401 wl_list_for_each_safe(cl, next, &d->clients, link) {
402 assert(cl->wl_client == NULL);
403
404 if (cl->exit_code != 0) {
405 ++failed;
406 fprintf(stderr, format: "Client '%s' failed\n", cl->name);
407 }
408
409 close(fd: cl->pipe);
410 free(ptr: cl);
411 }
412
413 wl_global_destroy(global: d->test_global);
414 wl_display_destroy(display: d->wl_display);
415 free(ptr: d);
416
417 if (failed) {
418 fprintf(stderr, format: "%d child(ren) failed\n", failed);
419 abort();
420 }
421}
422
423/*
424 * --- Client helper functions ---
425 */
426static void
427handle_display_resumed(void *data, struct test_compositor *tc)
428{
429 struct client *c = data;
430
431 c->display_stopped = 0;
432}
433
434static const struct test_compositor_listener tc_listener = {
435 handle_display_resumed
436};
437
438static void
439registry_handle_globals(void *data, struct wl_registry *registry,
440 uint32_t id, const char *intf, uint32_t ver)
441{
442 struct client *c = data;
443
444 if (strcmp(s1: intf, s2: "test") != 0)
445 return;
446
447 c->tc = wl_registry_bind(wl_registry: registry, name: id, interface: &test_compositor_interface, version: ver);
448 assert(c->tc && "Failed binding to registry");
449
450 wl_proxy_add_listener(proxy: (struct wl_proxy *) c->tc,
451 implementation: (void *) &tc_listener, data: c);
452}
453
454static const struct wl_registry_listener registry_listener =
455{
456 registry_handle_globals,
457 NULL
458};
459
460struct client *client_connect()
461{
462 struct wl_registry *reg;
463 struct client *c = calloc(nmemb: 1, size: sizeof *c);
464 assert(c && "Out of memory");
465
466 c->wl_display = wl_display_connect(NULL);
467 assert(c->wl_display && "Failed connecting to display");
468
469 /* create test_compositor proxy. Do it with temporary
470 * registry so that client can define it's own listener later */
471 reg = wl_display_get_registry(wl_display: c->wl_display);
472 assert(reg);
473 wl_registry_add_listener(wl_registry: reg, listener: &registry_listener, data: c);
474 wl_display_roundtrip(display: c->wl_display);
475 assert(c->tc);
476
477 wl_registry_destroy(wl_registry: reg);
478
479 return c;
480}
481
482static void
483check_error(struct wl_display *display)
484{
485 uint32_t ec, id;
486 const struct wl_interface *intf;
487 int err;
488
489 err = wl_display_get_error(display);
490 /* write out message about protocol error */
491 if (err == EPROTO) {
492 ec = wl_display_get_protocol_error(display, interface: &intf, id: &id);
493 fprintf(stderr, format: "Client: Got protocol error %u on interface %s"
494 " (object %u)\n", ec, intf->name, id);
495 }
496
497 if (err) {
498 fprintf(stderr, format: "Client error: %s\n", strerror(errnum: err));
499 abort();
500 }
501}
502
503void
504client_disconnect(struct client *c)
505{
506 /* check for errors */
507 check_error(display: c->wl_display);
508
509 wl_proxy_destroy(proxy: (struct wl_proxy *) c->tc);
510 wl_display_disconnect(display: c->wl_display);
511 free(ptr: c);
512}
513
514/* num is number of clients that requests to stop display.
515 * Display is stopped after it receives num STOP_DISPLAY requests */
516int
517stop_display(struct client *c, int num)
518{
519 int n = 0;
520
521 c->display_stopped = 1;
522 wl_proxy_marshal(p: (struct wl_proxy *) c->tc, opcode: STOP_DISPLAY, num);
523
524 while (c->display_stopped && n >= 0) {
525 n = wl_display_dispatch(display: c->wl_display);
526 }
527
528 return n;
529}
530
531void
532noop_request(struct client *c)
533{
534 wl_proxy_marshal(p: (struct wl_proxy *) c->tc, opcode: TEST_NOOP);
535}
536

source code of gtk/subprojects/wayland/tests/test-compositor.c