1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 2From: Lloyd Pique <[email protected]> 3Date: Thu, 10 Mar 2022 17:44:32 -0800 4Subject: [PATCH 2/6] client: Add message observer interface 5 6Client message observers 2/6 7 8Introduce a client message observer interface, strongly resembling the server 9protocol logger interface added in commit 450f06e2. 10 11This means a new pair of public API functions: 12 13* wl_display_create_client_observer(): allows a client to register an observer 14 function, which is called for messages that are received or sent. 15 16* wl_client_observer_destroy() which destroys the observer created by the prior 17 function. 18 19With these changes, a client can set and clear an observer at run-time, and can 20use it to log client messages to a location other than stderr. 21 22The existing protocol-logger-test has also been revised and extended to demonstrate 23using the new API for test use, to validate the sequence of messages sent and 24received by the client, on top of the existing checks to do the same for the 25server messages. 26 27Signed-off-by: Lloyd Pique <[email protected]> 28 29diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h 30index ce91a6f..2aa72a4 100644 31--- a/src/wayland-client-core.h 32+++ b/src/wayland-client-core.h 33@@ -285,6 +285,104 @@ wl_display_read_events(struct wl_display *display); 34 void 35 wl_log_set_handler_client(wl_log_func_t handler); 36 37+/** 38+ * The message type. 39+ */ 40+enum wl_client_message_type { 41+ /** The message is a request */ 42+ WL_CLIENT_MESSAGE_REQUEST, 43+ 44+ /** The message is an event */ 45+ WL_CLIENT_MESSAGE_EVENT, 46+}; 47+ 48+/** 49+ * The message discard reason codes. 50+ */ 51+enum wl_client_message_discarded_reason { 52+ /** The message was handled normally, and not discarded. */ 53+ WL_CLIENT_MESSAGE_NOT_DISCARDED = 0, 54+ 55+ /** The target was not alive at dispatch time */ 56+ WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, 57+ 58+ /** The target had no listener or dispatcher */ 59+ WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH, 60+}; 61+ 62+/** 63+ * The structure used to communicate details about an observed message to the 64+ * registered observers. 65+ */ 66+struct wl_client_observed_message { 67+ /** The target for the message */ 68+ struct wl_proxy *proxy; 69+ 70+ /** The message opcode */ 71+ int message_opcode; 72+ 73+ /** The protocol message structure */ 74+ const struct wl_message *message; 75+ 76+ /** The count of arguments to the message */ 77+ int arguments_count; 78+ 79+ /** The argument array for the messagge */ 80+ const union wl_argument *arguments; 81+ 82+ /** The discard reason code */ 83+ enum wl_client_message_discarded_reason discarded_reason; 84+ 85+ /** 86+ * The discard reason string, or NULL if the event was not discarded. 87+ * 88+ * This string is only for convenience for a observer that does 89+ * logging. The string values should not be considered stable, and 90+ * are not localized. 91+ */ 92+ const char *discarded_reason_str; 93+}; 94+ 95+/** 96+ * The signature for a client message observer function, as registered with 97+ * wl_display_add_client_observer(). 98+ * 99+ * \param user_data \c user_data pointer given when the observer was 100+ * registered with \c wl_display_create_client_observer 101+ * \param type type of message 102+ * \param message details for the message 103+ */ 104+typedef void (*wl_client_message_observer_func_t)( 105+ void *user_data, enum wl_client_message_type type, 106+ const struct wl_client_observed_message *message); 107+ 108+/** \class wl_client_observer 109+ * 110+ * \brief Represents a client message observer 111+ * 112+ * A client observer allows the client to observe all request and event 113+ * message traffic to and from the client. For events, the observer is 114+ * also given a discard reason if the event wasn't handled. 115+ * 116+ * The typical use for the observer is to allow the client implementation to 117+ * do its own debug logging, as the default when setting WAYLAND_DEBUG is to 118+ * log to stderr. 119+ * 120+ * With this runtime call, the client can also enable and disable the observer 121+ * at any time. 122+ * 123+ * The protocol-logger-test.c file has an example of a logger implementation. 124+ */ 125+struct wl_client_observer; 126+ 127+struct wl_client_observer * 128+wl_display_create_client_observer(struct wl_display *display, 129+ wl_client_message_observer_func_t observer, 130+ void *user_data); 131+ 132+void 133+wl_client_observer_destroy(struct wl_client_observer *observer); 134+ 135 #ifdef __cplusplus 136 } 137 #endif 138diff --git a/src/wayland-client.c b/src/wayland-client.c 139index ae47307..04b4f60 100644 140--- a/src/wayland-client.c 141+++ b/src/wayland-client.c 142@@ -109,10 +109,19 @@ struct wl_display { 143 int reader_count; 144 uint32_t read_serial; 145 pthread_cond_t reader_cond; 146+ 147+ struct wl_list observers; 148 }; 149 150 /** \endcond */ 151 152+struct wl_client_observer { 153+ struct wl_list link; 154+ struct wl_display *display; 155+ wl_client_message_observer_func_t func; 156+ void *user_data; 157+}; 158+ 159 static int debug_client = 0; 160 161 /** 162@@ -151,6 +160,28 @@ adjust_closure_args_for_logging(struct wl_closure *closure, bool send) 163 } 164 } 165 166+/** 167+ * Maps the \c discard_reason to a string suitable for logging. 168+ * 169+ * \param discarded_reason reason for discard 170+ * \return A string describing the reason, or NULL. 171+ * 172+ */ 173+static const char * 174+get_discarded_reason_str( 175+ enum wl_client_message_discarded_reason discarded_reason) 176+{ 177+ switch (discarded_reason) { 178+ case WL_CLIENT_MESSAGE_NOT_DISCARDED: 179+ return NULL; 180+ case WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH: 181+ return "dead proxy on dispatch"; 182+ case WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH: 183+ return "no listener on dispatch"; 184+ } 185+ return NULL; 186+} 187+ 188 /** 189 * This function helps log closures from the client, assuming logging is 190 * enabled. 191@@ -158,16 +189,18 @@ adjust_closure_args_for_logging(struct wl_closure *closure, bool send) 192 * \param closure closure for the message 193 * \param proxy proxy for the message 194 * \param send true if this is closure is for a request 195- * \param discarded true if this is message is being discarded 196- * 197+ * \param discarded_reason reason if the message is being discarded, or 198+ * WL_CLIENT_MESSAGE_NOT_DISCARDED 199 */ 200 static void 201 closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send, 202- bool discarded) 203+ enum wl_client_message_discarded_reason discarded_reason) 204 { 205+ struct wl_display *display = proxy->display; 206+ const char *discarded_reason_str; 207 struct wl_closure adjusted_closure = { 0 }; 208 209- if (!debug_client) 210+ if (!debug_client && wl_list_empty(&display->observers)) 211 return; 212 213 // Note: The real closure has extra data (referenced by its args 214@@ -178,8 +211,30 @@ closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send, 215 // Adjust the closure arguments. 216 adjust_closure_args_for_logging(&adjusted_closure, send); 217 218- wl_closure_print(&adjusted_closure, &proxy->object, send, 219- discarded ? "" : NULL); 220+ discarded_reason_str = get_discarded_reason_str(discarded_reason); 221+ 222+ if (debug_client) 223+ wl_closure_print(&adjusted_closure, &proxy->object, send, 224+ discarded_reason_str); 225+ 226+ if (!wl_list_empty(&display->observers)) { 227+ enum wl_client_message_type type = 228+ send ? WL_CLIENT_MESSAGE_REQUEST 229+ : WL_CLIENT_MESSAGE_EVENT; 230+ struct wl_client_observer *observer; 231+ struct wl_client_observed_message message; 232+ 233+ message.proxy = proxy; 234+ message.message_opcode = adjusted_closure.opcode; 235+ message.message = adjusted_closure.message; 236+ message.arguments_count = adjusted_closure.count; 237+ message.arguments = adjusted_closure.args; 238+ message.discarded_reason = discarded_reason; 239+ message.discarded_reason_str = discarded_reason_str; 240+ wl_list_for_each(observer, &display->observers, link) { 241+ observer->func(observer->user_data, type, &message); 242+ } 243+ } 244 } 245 246 /** 247@@ -952,7 +1007,7 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, 248 goto err_unlock; 249 } 250 251- closure_log(closure, proxy, true, false); 252+ closure_log(closure, proxy, true, WL_CLIENT_MESSAGE_NOT_DISCARDED); 253 254 if (wl_closure_send(closure, proxy->display->connection)) { 255 wl_log("Error sending request: %s\n", strerror(errno)); 256@@ -1259,6 +1314,7 @@ wl_display_connect_to_fd(int fd) 257 pthread_mutex_init(&display->mutex, NULL); 258 pthread_cond_init(&display->reader_cond, NULL); 259 display->reader_count = 0; 260+ wl_list_init(&display->observers); 261 262 if (wl_map_insert_at(&display->objects, 0, 0, NULL) == -1) 263 goto err_connection; 264@@ -1388,6 +1444,7 @@ wl_display_disconnect(struct wl_display *display) 265 wl_map_release(&display->objects); 266 wl_event_queue_release(&display->default_queue); 267 wl_event_queue_release(&display->display_queue); 268+ wl_list_remove(&display->observers); 269 pthread_mutex_destroy(&display->mutex); 270 pthread_cond_destroy(&display->reader_cond); 271 close(display->fd); 272@@ -1663,25 +1720,29 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue) 273 proxy = closure->proxy; 274 proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED); 275 if (proxy_destroyed) { 276- closure_log(closure, proxy, false, true); 277- destroy_queued_closure(closure); 278- return; 279- } 280- 281- pthread_mutex_unlock(&display->mutex); 282+ closure_log(closure, proxy, false, 283+ WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH); 284+ } else if (proxy->dispatcher) { 285+ closure_log(closure, proxy, false, 286+ WL_CLIENT_MESSAGE_NOT_DISCARDED); 287 288- if (proxy->dispatcher) { 289- closure_log(closure, proxy, false, false); 290+ pthread_mutex_unlock(&display->mutex); 291 wl_closure_dispatch(closure, proxy->dispatcher, 292 &proxy->object, opcode); 293+ pthread_mutex_lock(&display->mutex); 294 } else if (proxy->object.implementation) { 295- closure_log(closure, proxy, false, false); 296+ closure_log(closure, proxy, false, 297+ WL_CLIENT_MESSAGE_NOT_DISCARDED); 298+ 299+ pthread_mutex_unlock(&display->mutex); 300 wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT, 301 &proxy->object, opcode, proxy->user_data); 302+ pthread_mutex_lock(&display->mutex); 303+ } else { 304+ closure_log(closure, proxy, false, 305+ WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH); 306 } 307 308- pthread_mutex_lock(&display->mutex); 309- 310 destroy_queued_closure(closure); 311 } 312 313@@ -2538,3 +2599,64 @@ wl_log_set_handler_client(wl_log_func_t handler) 314 { 315 wl_log_handler = handler; 316 } 317+ 318+/** Creates an client message observer. 319+ * 320+ * Note that the observer can potentially start receiving traffic immediately 321+ * after being created, and even before this call returns. 322+ * 323+ * \param display client display to register with 324+ * \param func function to call when client messages are observed 325+ * \param user_data \c user_data pointer to pass to the observer 326+ * 327+ * \return The created observer, or NULL. 328+ * 329+ * \sa wl_client_observer_destroy 330+ * 331+ * \memberof wl_display 332+ */ 333+ 334+WL_EXPORT struct wl_client_observer * 335+wl_display_create_client_observer(struct wl_display *display, 336+ wl_client_message_observer_func_t func, 337+ void *user_data) 338+{ 339+ struct wl_client_observer *observer; 340+ 341+ observer = malloc(sizeof *observer); 342+ if (!observer) 343+ return NULL; 344+ 345+ observer->display = display; 346+ observer->func = func; 347+ observer->user_data = user_data; 348+ 349+ pthread_mutex_lock(&display->mutex); 350+ 351+ wl_list_insert(&display->observers, &observer->link); 352+ 353+ pthread_mutex_unlock(&display->mutex); 354+ 355+ return observer; 356+} 357+ 358+/** Destroys a client message obsever. 359+ * 360+ * This function destroys a client message observer, and removes it from the 361+ * display it was added to with \c wl_display_create_client_observer. 362+ * 363+ * \param observer observer to destroy. 364+ * 365+ * \memberof wl_client_observer 366+ */ 367+WL_EXPORT void 368+wl_client_observer_destroy(struct wl_client_observer *observer) 369+{ 370+ pthread_mutex_lock(&observer->display->mutex); 371+ 372+ wl_list_remove(&observer->link); 373+ 374+ pthread_mutex_unlock(&observer->display->mutex); 375+ 376+ free(observer); 377+} 378diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c 379index a0ebd22..3b9dc3e 100644 380--- a/tests/protocol-logger-test.c 381+++ b/tests/protocol-logger-test.c 382@@ -29,12 +29,15 @@ 383 #include <string.h> 384 #include <stdio.h> 385 #include <sys/un.h> 386+#include <time.h> 387 #include <unistd.h> 388 389 #include "wayland-client.h" 390 #include "wayland-server.h" 391 #include "test-runner.h" 392 393+#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) 394+ 395 /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */ 396 static const char * 397 require_xdg_runtime_dir(void) 398@@ -45,57 +48,146 @@ require_xdg_runtime_dir(void) 399 return val; 400 } 401 402+struct expected_compositor_message { 403+ enum wl_protocol_logger_type type; 404+ const char *class; 405+ int opcode; 406+ const char *message_name; 407+ int args_count; 408+}; 409+ 410 struct compositor { 411 struct wl_display *display; 412 struct wl_event_loop *loop; 413- int message; 414+ struct wl_protocol_logger *logger; 415+ 416+ struct expected_compositor_message *expected_msg; 417+ int expected_msg_count; 418+ int actual_msg_count; 419 struct wl_client *client; 420 }; 421 422-struct message { 423- enum wl_protocol_logger_type type; 424+struct expected_client_message { 425+ enum wl_client_message_type type; 426+ enum wl_client_message_discarded_reason discarded_reason; 427 const char *class; 428 int opcode; 429 const char *message_name; 430 int args_count; 431-} messages[] = { 432- { 433- .type = WL_PROTOCOL_LOGGER_REQUEST, 434- .class = "wl_display", 435- .opcode = 0, 436- .message_name = "sync", 437- .args_count = 1, 438- }, 439- { 440- .type = WL_PROTOCOL_LOGGER_EVENT, 441- .class = "wl_callback", 442- .opcode = 0, 443- .message_name = "done", 444- .args_count = 1, 445- }, 446- { 447- .type = WL_PROTOCOL_LOGGER_EVENT, 448- .class = "wl_display", 449- .opcode = 1, 450- .message_name = "delete_id", 451- .args_count = 1, 452- }, 453 }; 454 455+struct client { 456+ struct wl_display *display; 457+ struct wl_callback *cb; 458+ struct wl_client_observer *sequence_observer; 459+ 460+ struct expected_client_message *expected_msg; 461+ int expected_msg_count; 462+ int actual_msg_count; 463+}; 464+ 465+#define ASSERT_LT(arg1, arg2, ...) \ 466+ if (arg1 >= arg2) \ 467+ fprintf(stderr, __VA_ARGS__); \ 468+ assert(arg1 < arg2) 469+ 470+#define ASSERT_EQ(arg1, arg2, ...) \ 471+ if (arg1 != arg2) \ 472+ fprintf(stderr, __VA_ARGS__); \ 473+ assert(arg1 == arg2) 474+ 475+#define ASSERT_STR_EQ(arg1, arg2, ...) \ 476+ if (strcmp(arg1, arg2) != 0) \ 477+ fprintf(stderr, __VA_ARGS__); \ 478+ assert(strcmp(arg1, arg2) == 0) 479+ 480 static void 481-logger_func(void *user_data, enum wl_protocol_logger_type type, 482- const struct wl_protocol_logger_message *message) 483+compositor_sequence_observer_func( 484+ void *user_data, enum wl_protocol_logger_type actual_type, 485+ const struct wl_protocol_logger_message *actual_msg) 486 { 487 struct compositor *c = user_data; 488- struct message *msg = &messages[c->message++]; 489+ struct expected_compositor_message *expected_msg; 490+ int actual_msg_count = c->actual_msg_count++; 491+ char details_msg[256]; 492+ 493+ c->client = wl_resource_get_client(actual_msg->resource); 494+ 495+ if (!c->expected_msg) 496+ return; 497+ 498+ ASSERT_LT(actual_msg_count, c->expected_msg_count, 499+ "actual count %d exceeds expected count %d\n", 500+ actual_msg_count, c->expected_msg_count); 501+ 502+ expected_msg = &c->expected_msg[actual_msg_count]; 503+ 504+ snprintf(details_msg, sizeof details_msg, 505+ "compositor msg %d of %d actual [%d, '%s', %d, '%s', %d] vs " 506+ "expected [%d, '%s', %d, '%s', %d]\n", 507+ c->actual_msg_count, c->expected_msg_count, actual_type, 508+ wl_resource_get_class(actual_msg->resource), 509+ actual_msg->message_opcode, actual_msg->message->name, 510+ actual_msg->arguments_count, expected_msg->type, 511+ expected_msg->class, expected_msg->opcode, 512+ expected_msg->message_name, expected_msg->args_count); 513 514- assert(msg->type == type); 515- assert(strcmp(msg->class, wl_resource_get_class(message->resource)) == 0); 516- assert(msg->opcode == message->message_opcode); 517- assert(strcmp(msg->message_name, message->message->name) == 0); 518- assert(msg->args_count == message->arguments_count); 519+ ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s", 520+ details_msg); 521+ ASSERT_STR_EQ(expected_msg->class, 522+ wl_resource_get_class(actual_msg->resource), 523+ "class mismatch: %s", details_msg); 524+ ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode, 525+ "opcode mismatch: %s", details_msg); 526+ ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name, 527+ "message name mismatch: %s", details_msg); 528+ ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count, 529+ "arg count mismatch: %s", details_msg); 530+} 531+ 532+static void 533+client_sequence_observer_func( 534+ void *user_data, enum wl_client_message_type actual_type, 535+ const struct wl_client_observed_message *actual_msg) 536+{ 537+ struct client *c = user_data; 538+ struct expected_client_message *expected_msg; 539+ int actual_msg_count = c->actual_msg_count++; 540+ char details_msg[256]; 541+ 542+ if (!c->expected_msg) 543+ return; 544+ 545+ ASSERT_LT(actual_msg_count, c->expected_msg_count, 546+ "actual count %d exceeds expected count %d\n", 547+ actual_msg_count, c->expected_msg_count); 548+ expected_msg = &c->expected_msg[actual_msg_count]; 549 550- c->client = wl_resource_get_client(message->resource); 551+ snprintf(details_msg, sizeof details_msg, 552+ "client msg %d of %d actual [%d, %d, '%s', %d, '%s', %d] vs " 553+ "expected [%d, %d, '%s', %d, '%s', %d]\n", 554+ c->actual_msg_count, c->expected_msg_count, actual_type, 555+ actual_msg->discarded_reason, 556+ wl_proxy_get_class(actual_msg->proxy), 557+ actual_msg->message_opcode, actual_msg->message->name, 558+ actual_msg->arguments_count, expected_msg->type, 559+ expected_msg->discarded_reason, expected_msg->class, 560+ expected_msg->opcode, expected_msg->message_name, 561+ expected_msg->args_count); 562+ 563+ ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s", 564+ details_msg); 565+ ASSERT_EQ(expected_msg->discarded_reason, actual_msg->discarded_reason, 566+ "discarded reason mismatch: %s", details_msg); 567+ ASSERT_STR_EQ(expected_msg->class, 568+ wl_proxy_get_class(actual_msg->proxy), 569+ "class mismatch: %s", details_msg); 570+ ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode, 571+ "opcode mismatch: %s", details_msg); 572+ ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name, 573+ "message name mismatch: %s", details_msg); 574+ ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count, 575+ "arg count mismatch: %s", details_msg); 576 } 577 578 static void 579@@ -108,41 +200,236 @@ static const struct wl_callback_listener callback_listener = { 580 callback_done, 581 }; 582 583+static void 584+logger_setup(struct compositor *compositor, struct client *client) 585+{ 586+ const char *socket; 587+ 588+ require_xdg_runtime_dir(); 589+ 590+ compositor->display = wl_display_create(); 591+ compositor->loop = wl_display_get_event_loop(compositor->display); 592+ socket = wl_display_add_socket_auto(compositor->display); 593+ 594+ compositor->logger = wl_display_add_protocol_logger( 595+ compositor->display, compositor_sequence_observer_func, 596+ compositor); 597+ 598+ client->display = wl_display_connect(socket); 599+ client->sequence_observer = wl_display_create_client_observer( 600+ client->display, client_sequence_observer_func, client); 601+} 602+ 603+static void 604+logger_teardown(struct compositor *compositor, struct client *client) 605+{ 606+ wl_client_observer_destroy(client->sequence_observer); 607+ wl_display_disconnect(client->display); 608+ 609+ wl_client_destroy(compositor->client); 610+ wl_protocol_logger_destroy(compositor->logger); 611+ wl_display_destroy(compositor->display); 612+} 613+ 614 TEST(logger) 615 { 616 test_set_timeout(1); 617 618- const char *socket; 619+ struct expected_compositor_message compositor_messages[] = { 620+ { 621+ .type = WL_PROTOCOL_LOGGER_REQUEST, 622+ .class = "wl_display", 623+ .opcode = 0, 624+ .message_name = "sync", 625+ .args_count = 1, 626+ }, 627+ { 628+ .type = WL_PROTOCOL_LOGGER_EVENT, 629+ .class = "wl_callback", 630+ .opcode = 0, 631+ .message_name = "done", 632+ .args_count = 1, 633+ }, 634+ { 635+ .type = WL_PROTOCOL_LOGGER_EVENT, 636+ .class = "wl_display", 637+ .opcode = 1, 638+ .message_name = "delete_id", 639+ .args_count = 1, 640+ }, 641+ }; 642+ struct expected_client_message client_messages[] = { 643+ { 644+ .type = WL_CLIENT_MESSAGE_REQUEST, 645+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 646+ .class = "wl_display", 647+ .opcode = 0, 648+ .message_name = "sync", 649+ .args_count = 1, 650+ }, 651+ { 652+ .type = WL_CLIENT_MESSAGE_EVENT, 653+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 654+ .class = "wl_display", 655+ .opcode = 1, 656+ .message_name = "delete_id", 657+ .args_count = 1, 658+ }, 659+ { 660+ .type = WL_CLIENT_MESSAGE_EVENT, 661+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 662+ .class = "wl_callback", 663+ .opcode = 0, 664+ .message_name = "done", 665+ .args_count = 1, 666+ }, 667+ }; 668 struct compositor compositor = { 0 }; 669- struct { 670- struct wl_display *display; 671- struct wl_callback *cb; 672- } client; 673- struct wl_protocol_logger *logger; 674+ struct client client = { 0 }; 675 676- require_xdg_runtime_dir(); 677+ logger_setup(&compositor, &client); 678 679- compositor.display = wl_display_create(); 680- compositor.loop = wl_display_get_event_loop(compositor.display); 681- socket = wl_display_add_socket_auto(compositor.display); 682+ compositor.expected_msg = &compositor_messages[0]; 683+ compositor.expected_msg_count = ARRAY_LENGTH(compositor_messages); 684 685- logger = wl_display_add_protocol_logger(compositor.display, 686- logger_func, &compositor); 687+ client.expected_msg = &client_messages[0]; 688+ client.expected_msg_count = ARRAY_LENGTH(client_messages); 689 690- client.display = wl_display_connect(socket); 691 client.cb = wl_display_sync(client.display); 692 wl_callback_add_listener(client.cb, &callback_listener, NULL); 693 wl_display_flush(client.display); 694 695- while (compositor.message < 3) { 696+ while (compositor.actual_msg_count < compositor.expected_msg_count) { 697 wl_event_loop_dispatch(compositor.loop, -1); 698 wl_display_flush_clients(compositor.display); 699 } 700 701- wl_display_dispatch(client.display); 702- wl_display_disconnect(client.display); 703+ while (client.actual_msg_count < client.expected_msg_count) { 704+ wl_display_dispatch(client.display); 705+ } 706+ 707+ logger_teardown(&compositor, &client); 708+} 709+ 710+TEST(client_discards_if_dead_on_dispatch) 711+{ 712+ test_set_timeout(1); 713+ 714+ struct expected_client_message client_messages[] = { 715+ { 716+ .type = WL_CLIENT_MESSAGE_REQUEST, 717+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 718+ .class = "wl_display", 719+ .opcode = 0, 720+ .message_name = "sync", 721+ .args_count = 1, 722+ }, 723+ { 724+ .type = WL_CLIENT_MESSAGE_EVENT, 725+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 726+ .class = "wl_display", 727+ .opcode = 1, 728+ .message_name = "delete_id", 729+ .args_count = 1, 730+ }, 731+ { 732+ .type = WL_CLIENT_MESSAGE_EVENT, 733+ .discarded_reason = 734+ WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, 735+ .class = "wl_callback", 736+ .opcode = 0, 737+ .message_name = "done", 738+ .args_count = 1, 739+ }, 740+ }; 741+ struct compositor compositor = { 0 }; 742+ struct client client = { 0 }; 743+ 744+ logger_setup(&compositor, &client); 745+ 746+ compositor.expected_msg_count = 3; 747+ 748+ client.expected_msg = &client_messages[0]; 749+ client.expected_msg_count = ARRAY_LENGTH(client_messages); 750+ 751+ client.cb = wl_display_sync(client.display); 752+ wl_callback_add_listener(client.cb, &callback_listener, NULL); 753+ wl_display_flush(client.display); 754+ 755+ while (compositor.actual_msg_count < compositor.expected_msg_count) { 756+ wl_event_loop_dispatch(compositor.loop, -1); 757+ wl_display_flush_clients(compositor.display); 758+ } 759+ 760+ wl_display_prepare_read(client.display); 761+ wl_display_read_events(client.display); 762+ 763+ // To get a WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, we 764+ // destroy the callback after reading client events, but before 765+ // dispatching them. 766+ wl_callback_destroy(client.cb); 767+ 768+ while (client.actual_msg_count < client.expected_msg_count) { 769+ wl_display_dispatch(client.display); 770+ } 771+ 772+ logger_teardown(&compositor, &client); 773+} 774+ 775+TEST(client_discards_if_no_listener_on_dispatch) 776+{ 777+ test_set_timeout(1); 778+ 779+ struct expected_client_message client_messages[] = { 780+ { 781+ .type = WL_CLIENT_MESSAGE_REQUEST, 782+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 783+ .class = "wl_display", 784+ .opcode = 0, 785+ .message_name = "sync", 786+ .args_count = 1, 787+ }, 788+ { 789+ .type = WL_CLIENT_MESSAGE_EVENT, 790+ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, 791+ .class = "wl_display", 792+ .opcode = 1, 793+ .message_name = "delete_id", 794+ .args_count = 1, 795+ }, 796+ { 797+ .type = WL_CLIENT_MESSAGE_EVENT, 798+ .discarded_reason = 799+ WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH, 800+ .class = "wl_callback", 801+ .opcode = 0, 802+ .message_name = "done", 803+ .args_count = 1, 804+ }, 805+ }; 806+ struct compositor compositor = { 0 }; 807+ struct client client = { 0 }; 808+ 809+ logger_setup(&compositor, &client); 810+ 811+ compositor.expected_msg_count = 3; 812+ 813+ client.expected_msg = &client_messages[0]; 814+ client.expected_msg_count = ARRAY_LENGTH(client_messages); 815+ 816+ client.cb = wl_display_sync(client.display); 817+ wl_display_flush(client.display); 818+ 819+ while (compositor.actual_msg_count < compositor.expected_msg_count) { 820+ wl_event_loop_dispatch(compositor.loop, -1); 821+ wl_display_flush_clients(compositor.display); 822+ } 823+ 824+ while (client.actual_msg_count < client.expected_msg_count) { 825+ wl_display_dispatch(client.display); 826+ } 827+ 828+ wl_callback_destroy(client.cb); 829 830- wl_client_destroy(compositor.client); 831- wl_protocol_logger_destroy(logger); 832- wl_display_destroy(compositor.display); 833+ logger_teardown(&compositor, &client); 834 } 835