1/*
2 * Copyright © 2012 Intel Corporation
3 * Copyright © 2013 Jason Ekstrand
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <stdarg.h>
31#include <stdint.h>
32#include <string.h>
33#include <assert.h>
34#include <sys/socket.h>
35#include <unistd.h>
36#include <errno.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <sys/mman.h>
40
41#include <pthread.h>
42#include <poll.h>
43
44#include "wayland-private.h"
45#include "wayland-server.h"
46#include "wayland-client.h"
47#include "test-runner.h"
48#include "test-compositor.h"
49
50#include "tests-server-protocol.h"
51#include "tests-client-protocol.h"
52
53struct display_destroy_listener {
54 struct wl_listener listener;
55 int done;
56};
57
58static void
59display_destroy_notify(struct wl_listener *l, void *data)
60{
61 struct display_destroy_listener *listener;
62
63 listener = wl_container_of(l, listener, listener);
64 listener->done = 1;
65}
66
67TEST(display_destroy_listener)
68{
69 struct wl_display *display;
70 struct display_destroy_listener a, b;
71
72 display = wl_display_create();
73 assert(display);
74
75 a.listener.notify = &display_destroy_notify;
76 a.done = 0;
77 wl_display_add_destroy_listener(display, listener: &a.listener);
78
79 assert(wl_display_get_destroy_listener(display, display_destroy_notify) ==
80 &a.listener);
81
82 b.listener.notify = display_destroy_notify;
83 b.done = 0;
84 wl_display_add_destroy_listener(display, listener: &b.listener);
85
86 wl_list_remove(elm: &a.listener.link);
87
88 wl_display_destroy(display);
89
90 assert(!a.done);
91 assert(b.done);
92}
93
94/* Fake 'client' which does not use wl_display_connect, and thus leaves the
95 * file descriptor passed through WAYLAND_SOCKET intact. This should not
96 * trigger an assertion in the leak check. */
97static void
98empty_client(void)
99{
100 return;
101}
102
103TEST(tc_leaks_tests)
104{
105 struct display *d = display_create();
106 client_create_noarg(d, empty_client);
107 display_run(d);
108 display_destroy(d);
109}
110
111/* This is how pre proxy-version registry binds worked,
112 * this should create a proxy that shares the display's
113 * version number: 0 */
114static void *
115old_registry_bind(struct wl_registry *wl_registry,
116 uint32_t name,
117 const struct wl_interface *interface,
118 uint32_t version)
119{
120 struct wl_proxy *id;
121
122 id = wl_proxy_marshal_constructor(
123 proxy: (struct wl_proxy *) wl_registry, WL_REGISTRY_BIND,
124 interface, name, interface->name, version, NULL);
125
126 return (void *) id;
127}
128
129struct handler_info {
130 struct wl_seat *seat;
131 uint32_t bind_version;
132 bool use_unversioned;
133};
134
135static void
136registry_handle_globals(void *data, struct wl_registry *registry,
137 uint32_t id, const char *intf, uint32_t ver)
138{
139 struct handler_info *hi = data;
140
141 /* This is only for the proxy version test */
142 if (hi->bind_version)
143 ver = hi->bind_version;
144
145 if (strcmp(s1: intf, s2: "wl_seat") == 0) {
146 if (hi->use_unversioned)
147 hi->seat = old_registry_bind(wl_registry: registry, name: id,
148 interface: &wl_seat_interface, version: ver);
149 else
150 hi->seat = wl_registry_bind(wl_registry: registry, name: id,
151 interface: &wl_seat_interface, version: ver);
152 assert(hi->seat);
153 }
154}
155
156static const struct wl_registry_listener registry_listener = {
157 registry_handle_globals,
158 NULL
159};
160
161static struct wl_seat *
162client_get_seat_with_info(struct client *c, struct handler_info *hi)
163{
164 struct wl_registry *reg = wl_display_get_registry(wl_display: c->wl_display);
165 assert(reg);
166
167 assert(hi);
168 hi->seat = NULL;
169 wl_registry_add_listener(wl_registry: reg, listener: &registry_listener, data: hi);
170 wl_display_roundtrip(display: c->wl_display);
171 assert(hi->seat);
172
173 wl_registry_destroy(wl_registry: reg);
174
175 return hi->seat;
176}
177
178static struct wl_seat *
179client_get_seat(struct client *c)
180{
181 struct handler_info hi;
182
183 hi.use_unversioned = false;
184 hi.bind_version = 0;
185
186 return client_get_seat_with_info(c, hi: &hi);
187}
188
189static void
190check_pending_error(struct client *c, struct wl_proxy *proxy)
191{
192 uint32_t ec, id;
193 int err;
194 const struct wl_interface *intf;
195
196 err = wl_display_get_error(display: c->wl_display);
197 assert(err == EPROTO);
198
199 ec = wl_display_get_protocol_error(display: c->wl_display, interface: &intf, id: &id);
200 assert(ec == 23);
201 assert(intf == &wl_seat_interface);
202 assert(id == wl_proxy_get_id(proxy));
203}
204
205static void
206check_for_error(struct client *c, struct wl_proxy *proxy)
207{
208 /* client should be disconnected */
209 assert(wl_display_roundtrip(c->wl_display) == -1);
210
211 check_pending_error(c, proxy);
212}
213
214static struct client_info *
215find_client_info(struct display *d, struct wl_client *client)
216{
217 struct client_info *ci;
218
219 wl_list_for_each(ci, &d->clients, link) {
220 if (ci->wl_client == client)
221 return ci;
222 }
223
224 return NULL;
225}
226
227static void
228bind_seat(struct wl_client *client, void *data,
229 uint32_t vers, uint32_t id)
230{
231 struct display *d = data;
232 struct client_info *ci;
233 struct wl_resource *res;
234
235 ci = find_client_info(d, client);
236 assert(ci);
237
238 res = wl_resource_create(client, interface: &wl_seat_interface, version: vers, id);
239 assert(res);
240
241 /* save the resource as client's info data,
242 * so that we can use it later */
243 ci->data = res;
244}
245
246static void
247client_disconnect_nocheck(struct client *c)
248{
249 wl_proxy_destroy(proxy: (struct wl_proxy *) c->tc);
250 wl_display_disconnect(display: c->wl_display);
251 free(ptr: c);
252}
253
254static void
255post_error_main(void)
256{
257 struct client *c = client_connect();
258 struct wl_seat *seat = client_get_seat(c);
259
260 /* stop display so that it can post the error.
261 * The function should return -1, because of the posted error */
262 assert(stop_display(c, 1) == -1);
263
264 /* display should have posted error, check it! */
265 check_for_error(c, proxy: (struct wl_proxy *) seat);
266
267 /* don't call client_disconnect(c), because then the test would be
268 * aborted due to checks for error in this function */
269 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
270 client_disconnect_nocheck(c);
271}
272
273TEST(post_error_to_one_client)
274{
275 struct display *d = display_create();
276 struct client_info *cl;
277
278 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
279 version: 1, data: d, bind: bind_seat);
280
281 cl = client_create_noarg(d, post_error_main);
282 display_run(d);
283
284 /* the display was stopped by client, so it can
285 * proceed in the code and post an error */
286 assert(cl->data);
287 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
288 code: 23, msg: "Dummy error");
289
290 /* this one should be ignored */
291 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
292 code: 21, msg: "Dummy error (ignore)");
293
294 display_resume(d);
295 display_destroy(d);
296}
297
298static void
299post_error_main2(void)
300{
301 struct client *c = client_connect();
302 struct wl_seat *seat = client_get_seat(c);
303
304 /* the error should not be posted for this client */
305 assert(stop_display(c, 2) >= 0);
306
307 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
308 client_disconnect(c);
309}
310
311static void
312post_error_main3(void)
313{
314 struct client *c = client_connect();
315 struct wl_seat *seat = client_get_seat(c);
316
317 assert(stop_display(c, 2) == -1);
318 check_for_error(c, proxy: (struct wl_proxy *) seat);
319
320 /* don't call client_disconnect(c), because then the test would be
321 * aborted due to checks for error in this function */
322 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
323 client_disconnect_nocheck(c);
324}
325
326/* all the testcases could be in one TEST, but splitting it
327 * apart is better for debugging when the test fails */
328TEST(post_error_to_one_from_two_clients)
329{
330 struct display *d = display_create();
331 struct client_info *cl;
332
333 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
334 version: 1, data: d, bind: bind_seat);
335
336 client_create_noarg(d, post_error_main2);
337 cl = client_create_noarg(d, post_error_main3);
338 display_run(d);
339
340 /* post error only to the second client */
341 assert(cl->data);
342 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
343 code: 23, msg: "Dummy error");
344 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
345 code: 21, msg: "Dummy error (ignore)");
346
347 display_resume(d);
348 display_destroy(d);
349}
350
351/* all the testcases could be in one TEST, but splitting it
352 * apart is better for debugging when the test fails */
353TEST(post_error_to_two_clients)
354{
355 struct display *d = display_create();
356 struct client_info *cl, *cl2;
357
358 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
359 version: 1, data: d, bind: bind_seat);
360
361 cl = client_create_noarg(d, post_error_main3);
362 cl2 = client_create_noarg(d, post_error_main3);
363
364 display_run(d);
365
366 /* Try to send the error to both clients */
367 assert(cl->data && cl2->data);
368 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
369 code: 23, msg: "Dummy error");
370 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
371 code: 21, msg: "Dummy error (ignore)");
372
373 wl_resource_post_error(resource: (struct wl_resource *) cl2->data,
374 code: 23, msg: "Dummy error");
375 wl_resource_post_error(resource: (struct wl_resource *) cl2->data,
376 code: 21, msg: "Dummy error (ignore)");
377
378 display_resume(d);
379 display_destroy(d);
380}
381
382static void
383post_nomem_main(void)
384{
385 struct client *c = client_connect();
386 struct wl_seat *seat = client_get_seat(c);
387
388 assert(stop_display(c, 1) == -1);
389 assert(wl_display_get_error(c->wl_display) == ENOMEM);
390
391 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
392 client_disconnect_nocheck(c);
393}
394
395TEST(post_nomem_tst)
396{
397 struct display *d = display_create();
398 struct client_info *cl;
399
400 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
401 version: 1, data: d, bind: bind_seat);
402
403 cl = client_create_noarg(d, post_nomem_main);
404 display_run(d);
405
406 assert(cl->data);
407 wl_resource_post_no_memory(resource: (struct wl_resource *) cl->data);
408 display_resume(d);
409
410 /* first client terminated. Run it again,
411 * but post no memory to client */
412 cl = client_create_noarg(d, post_nomem_main);
413 display_run(d);
414
415 assert(cl->data);
416 wl_client_post_no_memory(client: cl->wl_client);
417 display_resume(d);
418
419 display_destroy(d);
420}
421
422static void
423post_implementation_error_main(void)
424{
425 struct client *c = client_connect();
426 struct wl_seat *seat = client_get_seat(c);
427 uint32_t object_id, protocol_error;
428 const struct wl_interface *interface;
429
430 assert(stop_display(c, 1) == -1);
431 int err = wl_display_get_error(display: c->wl_display);
432 fprintf(stderr, format: "Err is %i\n", err);
433 assert(err == EPROTO);
434 protocol_error = wl_display_get_protocol_error(display: c->wl_display,
435 interface: &interface,
436 id: &object_id);
437 assert(protocol_error == WL_DISPLAY_ERROR_IMPLEMENTATION);
438 assert(interface == &wl_display_interface);
439
440 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
441 client_disconnect_nocheck(c);
442}
443
444TEST(post_internal_error_tst)
445{
446 struct display *d = display_create();
447 struct client_info *cl;
448
449 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
450 version: 1, data: d, bind: bind_seat);
451
452 cl = client_create_noarg(d, post_implementation_error_main);
453 display_run(d);
454
455 wl_client_post_implementation_error(client: cl->wl_client, msg: "Error %i", 20);
456
457 display_resume(d);
458
459 display_destroy(d);
460}
461
462static void
463register_reading(struct wl_display *display)
464{
465 while(wl_display_prepare_read(display) != 0 && errno == EAGAIN)
466 assert(wl_display_dispatch_pending(display) >= 0);
467 assert(wl_display_flush(display) >= 0);
468}
469
470/* create thread that will call prepare+read so that
471 * it will block */
472static pthread_t
473create_thread(struct client *c, void *(*func)(void*))
474{
475 pthread_t thread;
476
477 c->display_stopped = 0;
478 /* func must set display->stopped to 1 before sleeping */
479 assert(pthread_create(&thread, NULL, func, c) == 0);
480
481 /* make sure the thread is sleeping. It's a little bit racy
482 * (setting display_stopped to 1 and calling wl_display_read_events)
483 * so call usleep once again after the loop ends - it should
484 * be sufficient... */
485 while (c->display_stopped == 0)
486 test_usleep(500);
487 test_usleep(10000);
488
489 return thread;
490}
491
492static void *
493thread_read_error(void *data)
494{
495 struct client *c = data;
496
497 register_reading(display: c->wl_display);
498
499 /*
500 * Calling the read right now will block this thread
501 * until the other thread will read the data.
502 * However, after invoking an error, this
503 * thread should be woken up or it will block indefinitely.
504 */
505 c->display_stopped = 1;
506 assert(wl_display_read_events(c->wl_display) == -1);
507
508 assert(wl_display_dispatch_pending(c->wl_display) == -1);
509 assert(wl_display_get_error(c->wl_display));
510
511 pthread_exit(NULL);
512}
513
514/* test posting an error in multi-threaded environment. */
515static void
516threading_post_err(void)
517{
518 DISABLE_LEAK_CHECKS;
519
520 struct client *c = client_connect();
521 pthread_t thread;
522
523 /* register read intention */
524 register_reading(display: c->wl_display);
525
526 /* use this var as an indicator that thread is sleeping */
527 c->display_stopped = 0;
528
529 /* create new thread that will register its intention too */
530 thread = create_thread(c, func: thread_read_error);
531
532 /* so now we have sleeping thread waiting for a pthread_cond signal.
533 * The main thread must call wl_display_read_events().
534 * If this call fails, then it won't call broadcast at the
535 * end of the function and the sleeping thread will block indefinitely.
536 * Make the call fail and watch if libwayland will unblock the thread! */
537
538 /* create error on fd, so that wl_display_read_events will fail.
539 * The same can happen when server hangs up */
540 close(fd: wl_display_get_fd(display: c->wl_display));
541 /* this read events will fail and will
542 * post an error that should wake the sleeping thread
543 * and dispatch the incoming events */
544 assert(wl_display_read_events(c->wl_display) == -1);
545
546 /* kill test in 3 seconds. This should be enough time for the
547 * thread to exit if it's not blocking. If everything is OK, than
548 * the thread was woken up and the test will end before the SIGALRM */
549 test_set_timeout(3);
550 pthread_join(th: thread, NULL);
551
552 client_disconnect_nocheck(c);
553}
554
555TEST(threading_errors_tst)
556{
557 struct display *d = display_create();
558
559 client_create_noarg(d, threading_post_err);
560 display_run(d);
561
562 display_destroy(d);
563}
564
565static void *
566thread_prepare_and_read(void *data)
567{
568 struct client *c = data;
569
570 register_reading(display: c->wl_display);
571
572 c->display_stopped = 1;
573
574 assert(wl_display_read_events(c->wl_display) == 0);
575 assert(wl_display_dispatch_pending(c->wl_display) == 0);
576
577 pthread_exit(NULL);
578}
579
580/* test cancel read*/
581static void
582threading_cancel_read(void)
583{
584 DISABLE_LEAK_CHECKS;
585
586 struct client *c = client_connect();
587 pthread_t th1, th2, th3;
588
589 register_reading(display: c->wl_display);
590
591 th1 = create_thread(c, func: thread_prepare_and_read);
592 th2 = create_thread(c, func: thread_prepare_and_read);
593 th3 = create_thread(c, func: thread_prepare_and_read);
594
595 /* all the threads are sleeping, waiting until read or cancel
596 * is called. Cancel the read and let the threads proceed */
597 wl_display_cancel_read(display: c->wl_display);
598
599 /* kill test in 3 seconds. This should be enough time for the
600 * thread to exit if it's not blocking. If everything is OK, than
601 * the thread was woken up and the test will end before the SIGALRM */
602 test_set_timeout(3);
603 pthread_join(th: th1, NULL);
604 pthread_join(th: th2, NULL);
605 pthread_join(th: th3, NULL);
606
607 client_disconnect(c);
608}
609
610TEST(threading_cancel_read_tst)
611{
612 struct display *d = display_create();
613
614 client_create_noarg(d, threading_cancel_read);
615 display_run(d);
616
617 display_destroy(d);
618}
619
620static void
621threading_read_eagain(void)
622{
623 DISABLE_LEAK_CHECKS;
624
625 struct client *c = client_connect();
626 pthread_t th1, th2, th3;
627
628 register_reading(display: c->wl_display);
629
630 th1 = create_thread(c, func: thread_prepare_and_read);
631 th2 = create_thread(c, func: thread_prepare_and_read);
632 th3 = create_thread(c, func: thread_prepare_and_read);
633
634 /* All the threads are sleeping, waiting until read or cancel
635 * is called. Since we have no data on socket waiting,
636 * the wl_connection_read should end up with error and set errno
637 * to EAGAIN. Check if the threads are woken up in this case. */
638 assert(wl_display_read_events(c->wl_display) == 0);
639 /* errno should be still set to EAGAIN if wl_connection_read
640 * set it - check if we're testing the right case */
641 assert(errno == EAGAIN);
642
643 test_set_timeout(3);
644 pthread_join(th: th1, NULL);
645 pthread_join(th: th2, NULL);
646 pthread_join(th: th3, NULL);
647
648 client_disconnect(c);
649}
650
651TEST(threading_read_eagain_tst)
652{
653 struct display *d = display_create();
654 client_create_noarg(d, threading_read_eagain);
655
656 display_run(d);
657
658 display_destroy(d);
659}
660
661static void *
662thread_prepare_and_read2(void *data)
663{
664 struct client *c = data;
665
666 while(wl_display_prepare_read(display: c->wl_display) != 0 && errno == EAGAIN)
667 assert(wl_display_dispatch_pending(c->wl_display) == -1);
668 assert(wl_display_flush(c->wl_display) == -1);
669
670 c->display_stopped = 1;
671
672 assert(wl_display_read_events(c->wl_display) == -1);
673 assert(wl_display_dispatch_pending(c->wl_display) == -1);
674
675 pthread_exit(NULL);
676}
677
678static void
679threading_read_after_error(void)
680{
681 DISABLE_LEAK_CHECKS;
682
683 struct client *c = client_connect();
684 pthread_t thread;
685
686 /* create an error */
687 close(fd: wl_display_get_fd(display: c->wl_display));
688 assert(wl_display_dispatch(c->wl_display) == -1);
689
690 /* try to prepare for reading */
691 while(wl_display_prepare_read(display: c->wl_display) != 0 && errno == EAGAIN)
692 assert(wl_display_dispatch_pending(c->wl_display) == -1);
693 assert(wl_display_flush(c->wl_display) == -1);
694
695 assert(pthread_create(&thread, NULL,
696 thread_prepare_and_read2, c) == 0);
697
698 /* make sure thread is sleeping */
699 while (c->display_stopped == 0)
700 test_usleep(500);
701 test_usleep(10000);
702
703 assert(wl_display_read_events(c->wl_display) == -1);
704
705 /* kill test in 3 seconds */
706 test_set_timeout(3);
707 pthread_join(th: thread, NULL);
708
709 client_disconnect_nocheck(c);
710}
711
712TEST(threading_read_after_error_tst)
713{
714 struct display *d = display_create();
715
716 client_create_noarg(d, threading_read_after_error);
717 display_run(d);
718
719 display_destroy(d);
720}
721
722static void
723wait_for_error_using_dispatch(struct client *c, struct wl_proxy *proxy)
724{
725 int ret;
726
727 while (true) {
728 /* Dispatching should eventually hit the protocol error before
729 * any other error. */
730 ret = wl_display_dispatch(display: c->wl_display);
731 if (ret == 0) {
732 continue;
733 } else {
734 assert(errno == EPROTO);
735 break;
736 }
737 }
738
739 check_pending_error(c, proxy);
740}
741
742static void
743wait_for_error_using_prepare_read(struct client *c, struct wl_proxy *proxy)
744{
745 int ret = 0;
746 struct pollfd pfd[2];
747
748 while (true) {
749 while (wl_display_prepare_read(display: c->wl_display) != 0 &&
750 errno == EAGAIN) {
751 assert(wl_display_dispatch_pending(c->wl_display) >= 0);
752 }
753
754 /* Flush may fail due to EPIPE if the connection is broken, but
755 * this must not set a fatal display error because that would
756 * result in it being impossible to read a potential protocol
757 * error. */
758 do {
759 ret = wl_display_flush(display: c->wl_display);
760 } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
761 assert(ret >= 0 || errno == EPIPE);
762 assert(wl_display_get_error(c->wl_display) == 0);
763
764 pfd[0].fd = wl_display_get_fd(display: c->wl_display);
765 pfd[0].events = POLLIN;
766 do {
767 ret = poll(fds: pfd, nfds: 1, timeout: -1);
768 } while (ret == -1 && errno == EINTR);
769 assert(ret != -1);
770
771 /* We should always manage to read the error before the EPIPE
772 * comes this way. */
773 assert(wl_display_read_events(c->wl_display) == 0);
774
775 /* Dispatching should eventually hit the protocol error before
776 * any other error. */
777 ret = wl_display_dispatch_pending(display: c->wl_display);
778 if (ret == 0) {
779 continue;
780 } else {
781 assert(errno == EPROTO);
782 break;
783 }
784 }
785
786 check_pending_error(c, proxy);
787}
788
789static void
790check_error_after_epipe(void *data)
791{
792 bool use_dispatch_helpers = *(bool *) data;
793 struct client *client;
794 struct wl_seat *seat;
795 struct wl_callback *callback;
796
797 client = client_connect();
798
799 /* This will, according to the implementation below, cause the server
800 * to post an error. */
801 seat = client_get_seat(c: client);
802 wl_display_flush(display: client->wl_display);
803
804 /* The server will not actually destroy the client until it receives
805 * input, so send something to trigger the client destruction. */
806 callback = wl_display_sync(wl_display: client->wl_display);
807 wl_callback_destroy(wl_callback: callback);
808
809 /* Sleep some to give the server a chance to react and destroy the
810 * client. */
811 test_usleep(200000);
812
813 /* Wait for the protocol error and check that we reached it before
814 * EPIPE. */
815 if (use_dispatch_helpers) {
816 wait_for_error_using_dispatch(c: client, proxy: (struct wl_proxy *) seat);
817 } else {
818 wait_for_error_using_prepare_read(c: client,
819 proxy: (struct wl_proxy *) seat);
820 }
821
822 wl_seat_destroy(wl_seat: seat);
823 client_disconnect_nocheck(c: client);
824}
825
826static void
827bind_seat_and_post_error(struct wl_client *client, void *data,
828 uint32_t version, uint32_t id)
829{
830 struct display *d = data;
831 struct client_info *ci;
832 struct wl_resource *resource;
833
834 ci = find_client_info(d, client);
835 assert(ci);
836
837 resource = wl_resource_create(client, interface: &wl_seat_interface, version, id);
838 assert(resource);
839 ci->data = resource;
840
841 wl_resource_post_error(resource: ci->data, code: 23, msg: "Dummy error");
842}
843
844TEST(error_code_after_epipe)
845{
846 struct display *d = display_create();
847 bool use_dispatch_helpers;
848
849 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
850 version: 1, data: d, bind: bind_seat_and_post_error);
851
852 use_dispatch_helpers = true;
853 client_create(d, check_error_after_epipe, &use_dispatch_helpers);
854 display_run(d);
855
856 use_dispatch_helpers = false;
857 client_create(d, check_error_after_epipe, &use_dispatch_helpers);
858 display_run(d);
859
860 display_destroy(d);
861}
862
863static void
864check_seat_versions(struct wl_seat *seat, uint32_t ev)
865{
866 struct wl_pointer *pointer;
867
868 assert(wl_proxy_get_version((struct wl_proxy *) seat) == ev);
869 assert(wl_seat_get_version(seat) == ev);
870
871 pointer = wl_seat_get_pointer(wl_seat: seat);
872 assert(wl_pointer_get_version(pointer) == ev);
873 assert(wl_proxy_get_version((struct wl_proxy *) pointer) == ev);
874 wl_proxy_destroy(proxy: (struct wl_proxy *) pointer);
875}
876
877/* Normal client with proxy versions available. */
878static void
879seat_version(void *data)
880{
881 struct handler_info *hi = data;
882 struct client *c = client_connect();
883 struct wl_seat *seat;
884
885 /* display proxy should always be version 0 */
886 assert(wl_proxy_get_version((struct wl_proxy *) c->wl_display) == 0);
887
888 seat = client_get_seat_with_info(c, hi);
889 if (hi->use_unversioned)
890 check_seat_versions(seat, ev: 0);
891 else
892 check_seat_versions(seat, ev: hi->bind_version);
893
894 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
895
896 client_disconnect_nocheck(c);
897}
898
899TEST(versions)
900{
901 struct display *d = display_create();
902 struct wl_global *global;
903 int i;
904
905 global = wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
906 version: 5, data: d, bind: bind_seat);
907
908 for (i = 1; i <= 5; i++) {
909 struct handler_info hi;
910
911 hi.bind_version = i;
912 hi.use_unversioned = false;
913 client_create(d, seat_version, &hi);
914 hi.use_unversioned = true;
915 client_create(d, seat_version, &hi);
916 }
917
918 display_run(d);
919
920 wl_global_destroy(global);
921
922 display_destroy(d);
923}
924
925static void
926check_error_on_destroyed_object(void *data)
927{
928 struct client *c;
929 struct wl_seat *seat;
930 uint32_t id;
931 const struct wl_interface *intf;
932
933 c = client_connect();
934 seat = client_get_seat(c);
935
936 /* destroy the seat proxy. The display won't know
937 * about it yet, so it will post the error as usual */
938 wl_proxy_destroy(proxy: (struct wl_proxy *) seat);
939
940 /* let display post the error. The error will
941 * be caught in stop_display while dispatching */
942 assert(stop_display(c, 1) == -1);
943
944 /* check the returned error. Since the object was destroyed,
945 * we don't know the interface and id */
946 assert(wl_display_get_error(c->wl_display) == EPROTO);
947 assert(wl_display_get_protocol_error(c->wl_display, &intf, &id) == 23);
948 assert(intf == NULL);
949 assert(id == 0);
950
951 client_disconnect_nocheck(c);
952}
953
954TEST(error_on_destroyed_object)
955{
956 struct client_info *cl;
957 struct display *d = display_create();
958
959 wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
960 version: 1, data: d, bind: bind_seat);
961
962 cl = client_create_noarg(d, check_error_on_destroyed_object);
963 display_run(d);
964
965 /* did client bind to the seat? */
966 assert(cl->data);
967
968 /* post error on the destroyed object */
969 wl_resource_post_error(resource: (struct wl_resource *) cl->data,
970 code: 23, msg: "Dummy error");
971 display_resume(d);
972 display_destroy(d);
973}
974
975static bool
976global_filter(const struct wl_client *client,
977 const struct wl_global *global,
978 void *data)
979{
980 /* Hide the wl_data_offer interface if no data was provided */
981 if (wl_global_get_interface(global) == &wl_data_offer_interface)
982 return data != NULL;
983
984 /* Show all the others */
985 return true;
986}
987
988static void
989bind_data_offer(struct wl_client *client, void *data,
990 uint32_t vers, uint32_t id)
991{
992 /* Client should not be able to bind to this interface! */
993 assert(false);
994}
995
996static void
997registry_handle_filtered(void *data, struct wl_registry *registry,
998 uint32_t id, const char *intf, uint32_t ver)
999{
1000 uint32_t *name = data;
1001
1002 if (strcmp (s1: intf, s2: "wl_data_offer") == 0) {
1003 assert(name);
1004 *name = id;
1005 }
1006}
1007
1008static const struct wl_registry_listener registry_listener_filtered = {
1009 registry_handle_filtered,
1010 NULL
1011};
1012
1013static void
1014get_globals(void *data)
1015{
1016 struct client *c = client_connect();
1017 struct wl_registry *registry;
1018
1019 registry = wl_display_get_registry(wl_display: c->wl_display);
1020 wl_registry_add_listener(wl_registry: registry, listener: &registry_listener_filtered, data);
1021 wl_display_roundtrip(display: c->wl_display);
1022
1023 wl_registry_destroy(wl_registry: registry);
1024 client_disconnect_nocheck(c);
1025}
1026
1027TEST(filtered_global_is_hidden)
1028{
1029 struct display *d;
1030 struct wl_global *g;
1031
1032 d = display_create();
1033
1034 g = wl_global_create(display: d->wl_display, interface: &wl_data_offer_interface,
1035 version: 1, data: d, bind: bind_data_offer);
1036 wl_display_set_global_filter(display: d->wl_display, filter: global_filter, NULL);
1037
1038 client_create_noarg(d, get_globals);
1039 display_run(d);
1040
1041 wl_global_destroy(global: g);
1042
1043 display_destroy(d);
1044}
1045
1046static void
1047check_bind_error(struct client *c)
1048{
1049 uint32_t errorcode, id;
1050 int err;
1051 const struct wl_interface *intf;
1052
1053 err = wl_display_get_error(display: c->wl_display);
1054 assert(err == EPROTO);
1055
1056 errorcode = wl_display_get_protocol_error(display: c->wl_display, interface: &intf, id: &id);
1057 assert(errorcode == WL_DISPLAY_ERROR_INVALID_OBJECT);
1058}
1059
1060static void
1061force_bind(void *data)
1062{
1063 struct client *c = client_connect();
1064 struct wl_registry *registry;
1065 void *ptr;
1066 uint32_t *name = data;
1067
1068 registry = wl_display_get_registry(wl_display: c->wl_display);
1069
1070 ptr = wl_registry_bind (wl_registry: registry, name: *name, interface: &wl_data_offer_interface, version: 1);
1071 wl_display_roundtrip(display: c->wl_display);
1072 check_bind_error(c);
1073
1074 wl_proxy_destroy(proxy: (struct wl_proxy *) ptr);
1075 wl_registry_destroy(wl_registry: registry);
1076
1077 client_disconnect_nocheck(c);
1078}
1079
1080TEST(bind_fails_on_filtered_global)
1081{
1082 struct display *d;
1083 struct wl_global *g;
1084 uint32_t *name;
1085
1086 /* Create a anonymous shared memory to pass the interface name */
1087 name = mmap(NULL, len: sizeof(uint32_t),
1088 PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, fd: -1, offset: 0);
1089
1090 d = display_create();
1091
1092 g = wl_global_create(display: d->wl_display, interface: &wl_data_offer_interface,
1093 version: 1, data: d, bind: bind_data_offer);
1094 wl_display_set_global_filter(display: d->wl_display, filter: global_filter, data: name);
1095
1096 client_create(d, get_globals, name);
1097 *name = 0;
1098
1099 display_run(d);
1100 /* wl_data_offer should be 2 */
1101 assert(*name == 2);
1102 wl_display_set_global_filter(display: d->wl_display, filter: global_filter, NULL);
1103
1104 /* Try to bind to the interface name when a global filter is in place */
1105 client_create(d, force_bind, name);
1106 display_run(d);
1107
1108 wl_global_destroy(global: g);
1109
1110 display_destroy(d);
1111}
1112
1113static void
1114pre_fd(void *data, struct fd_passer *fdp)
1115{
1116 fd_passer_destroy(fdp);
1117}
1118
1119static void
1120fd(void *data, struct fd_passer *fdp, int32_t fd)
1121{
1122 /* We destroyed the resource before this event */
1123 assert(false);
1124}
1125
1126struct fd_passer_listener fd_passer_listener = {
1127 pre_fd,
1128 fd,
1129};
1130
1131static void
1132zombie_fd_handle_globals(void *data, struct wl_registry *registry,
1133 uint32_t id, const char *intf, uint32_t ver)
1134{
1135 struct fd_passer *fdp;
1136
1137 if (!strcmp(s1: intf, s2: "fd_passer")) {
1138 fdp = wl_registry_bind(wl_registry: registry, name: id, interface: &fd_passer_interface, version: 1);
1139 fd_passer_add_listener(fdp, &fd_passer_listener, NULL);
1140 }
1141}
1142
1143static const struct wl_registry_listener zombie_fd_registry_listener = {
1144 zombie_fd_handle_globals,
1145 NULL
1146};
1147
1148static void
1149zombie_client(void *data)
1150{
1151 struct client *c = client_connect();
1152 struct wl_registry *registry;
1153
1154 registry = wl_display_get_registry(wl_display: c->wl_display);
1155 wl_registry_add_listener(wl_registry: registry, listener: &zombie_fd_registry_listener, NULL);
1156
1157 /* Gets the registry */
1158 wl_display_roundtrip(display: c->wl_display);
1159
1160 /* push out the fd_passer bind */
1161 wl_display_roundtrip(display: c->wl_display);
1162
1163 /* push out our fd_passer.destroy */
1164 wl_display_roundtrip(display: c->wl_display);
1165
1166 wl_registry_destroy(wl_registry: registry);
1167
1168 client_disconnect_nocheck(c);
1169}
1170
1171struct passer_data {
1172 struct wl_resource *conjoined_passer;
1173};
1174
1175static void
1176feed_pipe(int fd, char tosend)
1177{
1178 int count;
1179
1180 do {
1181 count = write(fd: fd, buf: &tosend, n: 1);
1182 } while (count != 1 && errno == EAGAIN);
1183 assert(count == 1);
1184 close(fd: fd);
1185}
1186
1187static void
1188fd_passer_clobber(struct wl_client *client, struct wl_resource *res)
1189{
1190 struct passer_data *pdata = wl_resource_get_user_data(resource: res);
1191 int pipes1[2], pipes2[2], ret;
1192
1193 if (pdata->conjoined_passer) {
1194 ret = pipe(pipedes: pipes1);
1195 assert(ret == 0);
1196 ret = pipe(pipedes: pipes2);
1197 assert(ret == 0);
1198
1199 wl_resource_queue_event(resource: res, opcode: FD_PASSER_FD, pipes1[0]);
1200 fd_passer_send_fd(pdata->conjoined_passer, pipes2[0]);
1201 feed_pipe(fd: pipes1[1], tosend: '1');
1202 feed_pipe(fd: pipes2[1], tosend: '2');
1203 close(fd: pipes1[0]);
1204 close(fd: pipes2[0]);
1205 }
1206 wl_resource_destroy(resource: res);
1207}
1208
1209static void
1210fd_passer_twin(struct wl_client *client, struct wl_resource *res, struct wl_resource *passer)
1211{
1212 struct passer_data *pdata = wl_resource_get_user_data(resource: res);
1213
1214 pdata->conjoined_passer = passer;
1215}
1216
1217static const struct fd_passer_interface fdp_interface = {
1218 fd_passer_clobber,
1219 fd_passer_twin
1220};
1221
1222static void
1223pdata_destroy(struct wl_resource *res)
1224{
1225 struct passer_data *pdata = wl_resource_get_user_data(resource: res);
1226
1227 free(ptr: pdata);
1228}
1229
1230static void
1231bind_fd_passer(struct wl_client *client, void *data,
1232 uint32_t vers, uint32_t id)
1233{
1234 struct wl_resource *res;
1235 struct passer_data *pdata;
1236
1237 pdata = malloc(size: sizeof(*pdata));
1238 assert(pdata);
1239 pdata->conjoined_passer = NULL;
1240
1241 res = wl_resource_create(client, interface: &fd_passer_interface, version: vers, id);
1242 wl_resource_set_implementation(resource: res, implementation: &fdp_interface, data: pdata, destroy: pdata_destroy);
1243 assert(res);
1244 if (vers == 1) {
1245 fd_passer_send_pre_fd(res);
1246 fd_passer_send_fd(res, fileno(stdin));
1247 }
1248}
1249
1250TEST(zombie_fd)
1251{
1252 struct display *d;
1253 struct wl_global *g;
1254
1255 d = display_create();
1256
1257 g = wl_global_create(display: d->wl_display, interface: &fd_passer_interface,
1258 version: 1, data: d, bind: bind_fd_passer);
1259
1260 client_create_noarg(d, zombie_client);
1261 display_run(d);
1262
1263 wl_global_destroy(global: g);
1264
1265 display_destroy(d);
1266}
1267
1268
1269static void
1270double_pre_fd(void *data, struct fd_passer *fdp)
1271{
1272 assert(false);
1273}
1274
1275static void
1276double_fd(void *data, struct fd_passer *fdp, int32_t fd)
1277{
1278 char buf;
1279 int count;
1280
1281 do {
1282 count = read(fd: fd, buf: &buf, nbytes: 1);
1283 } while (count != 1 && errno == EAGAIN);
1284 assert(count == 1);
1285
1286 close(fd: fd);
1287 fd_passer_destroy(fdp);
1288 assert(buf == '2');
1289}
1290
1291struct fd_passer_listener double_fd_passer_listener = {
1292 double_pre_fd,
1293 double_fd,
1294};
1295
1296
1297static void
1298double_zombie_fd_handle_globals(void *data, struct wl_registry *registry,
1299 uint32_t id, const char *intf, uint32_t ver)
1300{
1301 struct fd_passer *fdp1, *fdp2;
1302
1303 if (!strcmp(s1: intf, s2: "fd_passer")) {
1304 fdp1 = wl_registry_bind(wl_registry: registry, name: id, interface: &fd_passer_interface, version: 2);
1305 fd_passer_add_listener(fdp1, &double_fd_passer_listener, NULL);
1306 fdp2 = wl_registry_bind(wl_registry: registry, name: id, interface: &fd_passer_interface, version: 2);
1307 fd_passer_add_listener(fdp2, &double_fd_passer_listener, NULL);
1308 fd_passer_conjoin(fdp1, fdp2);
1309 fd_passer_destroy(fdp1);
1310 }
1311}
1312
1313static const struct wl_registry_listener double_zombie_fd_registry_listener = {
1314 double_zombie_fd_handle_globals,
1315 NULL
1316};
1317
1318static void
1319double_zombie_client(void *data)
1320{
1321 struct client *c = client_connect();
1322 struct wl_registry *registry;
1323
1324 registry = wl_display_get_registry(wl_display: c->wl_display);
1325 wl_registry_add_listener(wl_registry: registry, listener: &double_zombie_fd_registry_listener, NULL);
1326
1327 /* Gets the registry */
1328 wl_display_roundtrip(display: c->wl_display);
1329
1330 /* One more so server can respond to conjoin+destroy */
1331 wl_display_roundtrip(display: c->wl_display);
1332
1333 /* And finally push out our last fd_passer.destroy */
1334 wl_display_roundtrip(display: c->wl_display);
1335
1336 wl_registry_destroy(wl_registry: registry);
1337
1338 client_disconnect_nocheck(c);
1339}
1340
1341TEST(zombie_fd_errant_consumption)
1342{
1343 struct display *d;
1344 struct wl_global *g;
1345
1346 d = display_create();
1347
1348 g = wl_global_create(display: d->wl_display, interface: &fd_passer_interface,
1349 version: 2, data: d, bind: bind_fd_passer);
1350
1351 client_create_noarg(d, double_zombie_client);
1352 display_run(d);
1353
1354 wl_global_destroy(global: g);
1355
1356 display_destroy(d);
1357}
1358
1359
1360static void
1361registry_bind_interface_mismatch_handle_global(void *data,
1362 struct wl_registry *registry,
1363 uint32_t id, const char *intf,
1364 uint32_t ver)
1365{
1366 uint32_t *seat_id_ptr = data;
1367
1368 if (strcmp(s1: intf, s2: wl_seat_interface.name) == 0) {
1369 *seat_id_ptr = id;
1370 }
1371}
1372
1373static const struct wl_registry_listener bind_interface_mismatch_registry_listener = {
1374 registry_bind_interface_mismatch_handle_global,
1375 NULL
1376};
1377
1378static void
1379registry_bind_interface_mismatch_client(void *data)
1380{
1381 struct client *c = client_connect();
1382 struct wl_registry *registry;
1383 uint32_t seat_id = 0;
1384 void *ptr;
1385 int ret;
1386
1387 registry = wl_display_get_registry(wl_display: c->wl_display);
1388 wl_registry_add_listener(wl_registry: registry,
1389 listener: &bind_interface_mismatch_registry_listener,
1390 data: &seat_id);
1391
1392 ret = wl_display_roundtrip(display: c->wl_display);
1393 assert(ret >= 0);
1394 assert(seat_id != 0);
1395
1396 /* Bind with a different interface */
1397 ptr = wl_registry_bind(wl_registry: registry, name: seat_id, interface: &wl_output_interface, version: 1);
1398 ret = wl_display_roundtrip(display: c->wl_display);
1399 assert(ret < 0);
1400 check_bind_error(c);
1401
1402 wl_proxy_destroy(proxy: (struct wl_proxy *) ptr);
1403 wl_registry_destroy(wl_registry: registry);
1404
1405 client_disconnect_nocheck(c);
1406}
1407
1408TEST(registry_bind_interface_mismatch)
1409{
1410 struct display *d;
1411 struct wl_global *seat_global;
1412
1413 d = display_create();
1414
1415 seat_global = wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
1416 version: 1, NULL, NULL);
1417
1418 client_create_noarg(d, registry_bind_interface_mismatch_client);
1419 display_run(d);
1420
1421 wl_global_destroy(global: seat_global);
1422
1423 display_destroy(d);
1424}
1425
1426static void
1427send_overflow_client(void *data)
1428{
1429 struct client *c = client_connect();
1430 int i, err = 0;
1431 int *pipes = data;
1432 char tmp = '\0';
1433 int sock, optval = 16384;
1434
1435 /* Limit the send buffer size for the display socket to guarantee
1436 * that the test will cause an overflow. */
1437 sock = wl_display_get_fd(display: c->wl_display);
1438 assert(setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == 0);
1439
1440 /* Request to break out of 'display_run' in the main process */
1441 assert(stop_display(c, 1) >= 0);
1442
1443 /* On Linux, the actual socket data + metadata space is twice `optval`;
1444 * since each noop request requires 8 bytes, the buffer should overflow
1445 * within <=4096 iterations. */
1446 for (i = 0; i < 1000000; i++) {
1447 noop_request(c);
1448 err = wl_display_get_error(display: c->wl_display);
1449 if (err)
1450 break;
1451 }
1452
1453 /* Do not close the pipe file descriptors afterwards, because the leak
1454 * check verifies that the initial/final FD counts are the same */
1455 assert(write(pipes[1], &tmp, sizeof(tmp)) == (ssize_t)sizeof(tmp));
1456
1457 /* Expect an error */
1458 fprintf(stderr, format: "Send loop failed on try %d, err = %d, %s\n", i, err, strerror(errnum: err));
1459 assert(err == EAGAIN);
1460
1461 client_disconnect_nocheck(c);
1462}
1463
1464TEST(send_overflow_disconnection)
1465{
1466 struct display *d;
1467 char tmp;
1468 int rpipe[2];
1469 ssize_t ret;
1470
1471 assert(pipe(rpipe) != -1);
1472
1473 d = display_create();
1474
1475 (void) client_create(d, send_overflow_client, &rpipe);
1476
1477 /* Close write end of the pipe, so that the later read() call gets
1478 * interrupted if the client dies */
1479 close(fd: rpipe[1]);
1480
1481 /* Run the display until the client sends a `stop_display`, then
1482 * send a resume message but don't actually look at new messages */
1483 display_run(d);
1484 display_post_resume_events(d);
1485 wl_display_flush_clients(display: d->wl_display);
1486
1487 /* Wait until all noop requests have been sent (read returns 1), or
1488 * until client process aborts (read returns 0) */
1489 do {
1490 ret = read(fd: rpipe[0], buf: &tmp, nbytes: sizeof(tmp));
1491 } while (ret == -1 && errno == EINTR);
1492 assert(ret != -1);
1493 close(fd: rpipe[0]);
1494
1495 /* For a clean shutdown */
1496 display_run(d);
1497
1498 display_destroy(d);
1499}
1500
1501static void
1502registry_global_remove_before_handle_global(void *data,
1503 struct wl_registry *registry,
1504 uint32_t id, const char *intf,
1505 uint32_t ver)
1506{
1507 uint32_t *id_ptr = data;
1508
1509 if (strcmp(s1: intf, s2: wl_seat_interface.name) == 0) {
1510 assert(*id_ptr == 0);
1511 *id_ptr = id;
1512 }
1513}
1514
1515static void
1516registry_global_remove_before_handle_global_remove(void *data,
1517 struct wl_registry *registry,
1518 uint32_t id)
1519{
1520 uint32_t *id_ptr = data;
1521
1522 if (*id_ptr == id) {
1523 *id_ptr = 0;
1524 }
1525}
1526
1527/* This listener expects a uint32_t user data pointer, sets it to the wl_seat
1528 * global ID when receiving a "global" event, and sets it to zero when receiving
1529 * a "global_remove" event. */
1530static const struct wl_registry_listener global_remove_before_registry_listener = {
1531 registry_global_remove_before_handle_global,
1532 registry_global_remove_before_handle_global_remove,
1533};
1534
1535static void
1536global_remove_before_client(void *data)
1537{
1538 struct client *c = client_connect();
1539 struct wl_registry *registry;
1540 uint32_t global_id = 0, saved_global_id;
1541 struct wl_seat *seat;
1542 int ret;
1543
1544 registry = wl_display_get_registry(wl_display: c->wl_display);
1545 wl_registry_add_listener(wl_registry: registry,
1546 listener: &global_remove_before_registry_listener,
1547 data: &global_id);
1548
1549 ret = wl_display_roundtrip(display: c->wl_display);
1550 assert(ret >= 0);
1551 assert(global_id != 0);
1552 saved_global_id = global_id;
1553
1554 /* Wait for the compositor to remove the global */
1555 assert(stop_display(c, 1) >= 0);
1556
1557 /* Check binding still works after the global has been removed. Also
1558 * check we get the global_remove event. */
1559 seat = wl_registry_bind(wl_registry: registry, name: saved_global_id, interface: &wl_seat_interface, version: 1);
1560 ret = wl_display_roundtrip(display: c->wl_display);
1561 assert(ret >= 0);
1562 assert(global_id == 0);
1563
1564 wl_seat_destroy(wl_seat: seat);
1565 wl_registry_destroy(wl_registry: registry);
1566
1567 client_disconnect(c);
1568}
1569
1570static void
1571registry_global_remove_after_handle_global(void *data,
1572 struct wl_registry *registry,
1573 uint32_t id, const char *intf,
1574 uint32_t ver)
1575{
1576 /* Make sure the global isn't advertised anymore after being removed */
1577 assert(strcmp(intf, wl_seat_interface.name) != 0);
1578}
1579
1580static const struct wl_registry_listener global_remove_after_registry_listener = {
1581 registry_global_remove_after_handle_global,
1582 NULL,
1583};
1584
1585static void
1586global_remove_after_client(void *data)
1587{
1588 struct client *c = client_connect();
1589 struct wl_registry *registry;
1590 uint32_t global_id = 0;
1591 int ret;
1592
1593 registry = wl_display_get_registry(wl_display: c->wl_display);
1594 wl_registry_add_listener(wl_registry: registry,
1595 listener: &global_remove_after_registry_listener,
1596 data: &global_id);
1597
1598 ret = wl_display_roundtrip(display: c->wl_display);
1599 assert(ret >= 0);
1600
1601 wl_registry_destroy(wl_registry: registry);
1602
1603 client_disconnect(c);
1604}
1605
1606TEST(global_remove)
1607{
1608 struct display *d;
1609 struct wl_global *global;
1610
1611 d = display_create();
1612
1613 global = wl_global_create(display: d->wl_display, interface: &wl_seat_interface,
1614 version: 1, data: d, bind: bind_seat);
1615
1616 /* Create a client before removing the global */
1617 client_create_noarg(d, global_remove_before_client);
1618
1619 display_run(d);
1620
1621 wl_global_remove(global);
1622
1623 /* Create another client after removing the global */
1624 client_create_noarg(d, global_remove_after_client);
1625
1626 display_resume(d);
1627
1628 wl_global_destroy(global);
1629
1630 display_destroy(d);
1631}
1632
1633static void
1634terminate_display(void *arg)
1635{
1636 struct wl_display *wl_display = arg;
1637 wl_display_terminate(display: wl_display);
1638}
1639
1640TEST(no_source_terminate)
1641{
1642 struct display *d;
1643 struct wl_event_loop *loop;
1644
1645 d = display_create();
1646 loop = wl_display_get_event_loop(display: d->wl_display);
1647
1648 wl_event_loop_add_idle(loop, func: terminate_display, data: d->wl_display);
1649
1650 display_run(d);
1651 display_destroy(d);
1652}
1653

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