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