diff options
Diffstat (limited to 'patches/0002-client-Add-message-observer-interface.diff')
-rw-r--r-- | patches/0002-client-Add-message-observer-interface.diff | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/patches/0002-client-Add-message-observer-interface.diff b/patches/0002-client-Add-message-observer-interface.diff new file mode 100644 index 0000000..ce3bec1 --- /dev/null +++ b/patches/0002-client-Add-message-observer-interface.diff @@ -0,0 +1,834 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Lloyd Pique <lpique@google.com> +Date: Thu, 10 Mar 2022 17:44:32 -0800 +Subject: [PATCH 2/6] client: Add message observer interface + +Client message observers 2/6 + +Introduce a client message observer interface, strongly resembling the server +protocol logger interface added in commit 450f06e2. + +This means a new pair of public API functions: + +* wl_display_create_client_observer(): allows a client to register an observer + function, which is called for messages that are received or sent. + +* wl_client_observer_destroy() which destroys the observer created by the prior + function. + +With these changes, a client can set and clear an observer at run-time, and can +use it to log client messages to a location other than stderr. + +The existing protocol-logger-test has also been revised and extended to demonstrate +using the new API for test use, to validate the sequence of messages sent and +received by the client, on top of the existing checks to do the same for the +server messages. + +Signed-off-by: Lloyd Pique <lpique@google.com> + +diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h +index ce91a6f..2aa72a4 100644 +--- a/src/wayland-client-core.h ++++ b/src/wayland-client-core.h +@@ -285,6 +285,104 @@ wl_display_read_events(struct wl_display *display); + void + wl_log_set_handler_client(wl_log_func_t handler); + ++/** ++ * The message type. ++ */ ++enum wl_client_message_type { ++ /** The message is a request */ ++ WL_CLIENT_MESSAGE_REQUEST, ++ ++ /** The message is an event */ ++ WL_CLIENT_MESSAGE_EVENT, ++}; ++ ++/** ++ * The message discard reason codes. ++ */ ++enum wl_client_message_discarded_reason { ++ /** The message was handled normally, and not discarded. */ ++ WL_CLIENT_MESSAGE_NOT_DISCARDED = 0, ++ ++ /** The target was not alive at dispatch time */ ++ WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, ++ ++ /** The target had no listener or dispatcher */ ++ WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH, ++}; ++ ++/** ++ * The structure used to communicate details about an observed message to the ++ * registered observers. ++ */ ++struct wl_client_observed_message { ++ /** The target for the message */ ++ struct wl_proxy *proxy; ++ ++ /** The message opcode */ ++ int message_opcode; ++ ++ /** The protocol message structure */ ++ const struct wl_message *message; ++ ++ /** The count of arguments to the message */ ++ int arguments_count; ++ ++ /** The argument array for the messagge */ ++ const union wl_argument *arguments; ++ ++ /** The discard reason code */ ++ enum wl_client_message_discarded_reason discarded_reason; ++ ++ /** ++ * The discard reason string, or NULL if the event was not discarded. ++ * ++ * This string is only for convenience for a observer that does ++ * logging. The string values should not be considered stable, and ++ * are not localized. ++ */ ++ const char *discarded_reason_str; ++}; ++ ++/** ++ * The signature for a client message observer function, as registered with ++ * wl_display_add_client_observer(). ++ * ++ * \param user_data \c user_data pointer given when the observer was ++ * registered with \c wl_display_create_client_observer ++ * \param type type of message ++ * \param message details for the message ++ */ ++typedef void (*wl_client_message_observer_func_t)( ++ void *user_data, enum wl_client_message_type type, ++ const struct wl_client_observed_message *message); ++ ++/** \class wl_client_observer ++ * ++ * \brief Represents a client message observer ++ * ++ * A client observer allows the client to observe all request and event ++ * message traffic to and from the client. For events, the observer is ++ * also given a discard reason if the event wasn't handled. ++ * ++ * The typical use for the observer is to allow the client implementation to ++ * do its own debug logging, as the default when setting WAYLAND_DEBUG is to ++ * log to stderr. ++ * ++ * With this runtime call, the client can also enable and disable the observer ++ * at any time. ++ * ++ * The protocol-logger-test.c file has an example of a logger implementation. ++ */ ++struct wl_client_observer; ++ ++struct wl_client_observer * ++wl_display_create_client_observer(struct wl_display *display, ++ wl_client_message_observer_func_t observer, ++ void *user_data); ++ ++void ++wl_client_observer_destroy(struct wl_client_observer *observer); ++ + #ifdef __cplusplus + } + #endif +diff --git a/src/wayland-client.c b/src/wayland-client.c +index ae47307..04b4f60 100644 +--- a/src/wayland-client.c ++++ b/src/wayland-client.c +@@ -109,10 +109,19 @@ struct wl_display { + int reader_count; + uint32_t read_serial; + pthread_cond_t reader_cond; ++ ++ struct wl_list observers; + }; + + /** \endcond */ + ++struct wl_client_observer { ++ struct wl_list link; ++ struct wl_display *display; ++ wl_client_message_observer_func_t func; ++ void *user_data; ++}; ++ + static int debug_client = 0; + + /** +@@ -151,6 +160,28 @@ adjust_closure_args_for_logging(struct wl_closure *closure, bool send) + } + } + ++/** ++ * Maps the \c discard_reason to a string suitable for logging. ++ * ++ * \param discarded_reason reason for discard ++ * \return A string describing the reason, or NULL. ++ * ++ */ ++static const char * ++get_discarded_reason_str( ++ enum wl_client_message_discarded_reason discarded_reason) ++{ ++ switch (discarded_reason) { ++ case WL_CLIENT_MESSAGE_NOT_DISCARDED: ++ return NULL; ++ case WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH: ++ return "dead proxy on dispatch"; ++ case WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH: ++ return "no listener on dispatch"; ++ } ++ return NULL; ++} ++ + /** + * This function helps log closures from the client, assuming logging is + * enabled. +@@ -158,16 +189,18 @@ adjust_closure_args_for_logging(struct wl_closure *closure, bool send) + * \param closure closure for the message + * \param proxy proxy for the message + * \param send true if this is closure is for a request +- * \param discarded true if this is message is being discarded +- * ++ * \param discarded_reason reason if the message is being discarded, or ++ * WL_CLIENT_MESSAGE_NOT_DISCARDED + */ + static void + closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send, +- bool discarded) ++ enum wl_client_message_discarded_reason discarded_reason) + { ++ struct wl_display *display = proxy->display; ++ const char *discarded_reason_str; + struct wl_closure adjusted_closure = { 0 }; + +- if (!debug_client) ++ if (!debug_client && wl_list_empty(&display->observers)) + return; + + // Note: The real closure has extra data (referenced by its args +@@ -178,8 +211,30 @@ closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send, + // Adjust the closure arguments. + adjust_closure_args_for_logging(&adjusted_closure, send); + +- wl_closure_print(&adjusted_closure, &proxy->object, send, +- discarded ? "" : NULL); ++ discarded_reason_str = get_discarded_reason_str(discarded_reason); ++ ++ if (debug_client) ++ wl_closure_print(&adjusted_closure, &proxy->object, send, ++ discarded_reason_str); ++ ++ if (!wl_list_empty(&display->observers)) { ++ enum wl_client_message_type type = ++ send ? WL_CLIENT_MESSAGE_REQUEST ++ : WL_CLIENT_MESSAGE_EVENT; ++ struct wl_client_observer *observer; ++ struct wl_client_observed_message message; ++ ++ message.proxy = proxy; ++ message.message_opcode = adjusted_closure.opcode; ++ message.message = adjusted_closure.message; ++ message.arguments_count = adjusted_closure.count; ++ message.arguments = adjusted_closure.args; ++ message.discarded_reason = discarded_reason; ++ message.discarded_reason_str = discarded_reason_str; ++ wl_list_for_each(observer, &display->observers, link) { ++ observer->func(observer->user_data, type, &message); ++ } ++ } + } + + /** +@@ -952,7 +1007,7 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, + goto err_unlock; + } + +- closure_log(closure, proxy, true, false); ++ closure_log(closure, proxy, true, WL_CLIENT_MESSAGE_NOT_DISCARDED); + + if (wl_closure_send(closure, proxy->display->connection)) { + wl_log("Error sending request: %s\n", strerror(errno)); +@@ -1259,6 +1314,7 @@ wl_display_connect_to_fd(int fd) + pthread_mutex_init(&display->mutex, NULL); + pthread_cond_init(&display->reader_cond, NULL); + display->reader_count = 0; ++ wl_list_init(&display->observers); + + if (wl_map_insert_at(&display->objects, 0, 0, NULL) == -1) + goto err_connection; +@@ -1388,6 +1444,7 @@ wl_display_disconnect(struct wl_display *display) + wl_map_release(&display->objects); + wl_event_queue_release(&display->default_queue); + wl_event_queue_release(&display->display_queue); ++ wl_list_remove(&display->observers); + pthread_mutex_destroy(&display->mutex); + pthread_cond_destroy(&display->reader_cond); + close(display->fd); +@@ -1663,25 +1720,29 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue) + proxy = closure->proxy; + proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED); + if (proxy_destroyed) { +- closure_log(closure, proxy, false, true); +- destroy_queued_closure(closure); +- return; +- } +- +- pthread_mutex_unlock(&display->mutex); ++ closure_log(closure, proxy, false, ++ WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH); ++ } else if (proxy->dispatcher) { ++ closure_log(closure, proxy, false, ++ WL_CLIENT_MESSAGE_NOT_DISCARDED); + +- if (proxy->dispatcher) { +- closure_log(closure, proxy, false, false); ++ pthread_mutex_unlock(&display->mutex); + wl_closure_dispatch(closure, proxy->dispatcher, + &proxy->object, opcode); ++ pthread_mutex_lock(&display->mutex); + } else if (proxy->object.implementation) { +- closure_log(closure, proxy, false, false); ++ closure_log(closure, proxy, false, ++ WL_CLIENT_MESSAGE_NOT_DISCARDED); ++ ++ pthread_mutex_unlock(&display->mutex); + wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT, + &proxy->object, opcode, proxy->user_data); ++ pthread_mutex_lock(&display->mutex); ++ } else { ++ closure_log(closure, proxy, false, ++ WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH); + } + +- pthread_mutex_lock(&display->mutex); +- + destroy_queued_closure(closure); + } + +@@ -2538,3 +2599,64 @@ wl_log_set_handler_client(wl_log_func_t handler) + { + wl_log_handler = handler; + } ++ ++/** Creates an client message observer. ++ * ++ * Note that the observer can potentially start receiving traffic immediately ++ * after being created, and even before this call returns. ++ * ++ * \param display client display to register with ++ * \param func function to call when client messages are observed ++ * \param user_data \c user_data pointer to pass to the observer ++ * ++ * \return The created observer, or NULL. ++ * ++ * \sa wl_client_observer_destroy ++ * ++ * \memberof wl_display ++ */ ++ ++WL_EXPORT struct wl_client_observer * ++wl_display_create_client_observer(struct wl_display *display, ++ wl_client_message_observer_func_t func, ++ void *user_data) ++{ ++ struct wl_client_observer *observer; ++ ++ observer = malloc(sizeof *observer); ++ if (!observer) ++ return NULL; ++ ++ observer->display = display; ++ observer->func = func; ++ observer->user_data = user_data; ++ ++ pthread_mutex_lock(&display->mutex); ++ ++ wl_list_insert(&display->observers, &observer->link); ++ ++ pthread_mutex_unlock(&display->mutex); ++ ++ return observer; ++} ++ ++/** Destroys a client message obsever. ++ * ++ * This function destroys a client message observer, and removes it from the ++ * display it was added to with \c wl_display_create_client_observer. ++ * ++ * \param observer observer to destroy. ++ * ++ * \memberof wl_client_observer ++ */ ++WL_EXPORT void ++wl_client_observer_destroy(struct wl_client_observer *observer) ++{ ++ pthread_mutex_lock(&observer->display->mutex); ++ ++ wl_list_remove(&observer->link); ++ ++ pthread_mutex_unlock(&observer->display->mutex); ++ ++ free(observer); ++} +diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c +index a0ebd22..3b9dc3e 100644 +--- a/tests/protocol-logger-test.c ++++ b/tests/protocol-logger-test.c +@@ -29,12 +29,15 @@ + #include <string.h> + #include <stdio.h> + #include <sys/un.h> ++#include <time.h> + #include <unistd.h> + + #include "wayland-client.h" + #include "wayland-server.h" + #include "test-runner.h" + ++#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0]) ++ + /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */ + static const char * + require_xdg_runtime_dir(void) +@@ -45,57 +48,146 @@ require_xdg_runtime_dir(void) + return val; + } + ++struct expected_compositor_message { ++ enum wl_protocol_logger_type type; ++ const char *class; ++ int opcode; ++ const char *message_name; ++ int args_count; ++}; ++ + struct compositor { + struct wl_display *display; + struct wl_event_loop *loop; +- int message; ++ struct wl_protocol_logger *logger; ++ ++ struct expected_compositor_message *expected_msg; ++ int expected_msg_count; ++ int actual_msg_count; + struct wl_client *client; + }; + +-struct message { +- enum wl_protocol_logger_type type; ++struct expected_client_message { ++ enum wl_client_message_type type; ++ enum wl_client_message_discarded_reason discarded_reason; + const char *class; + int opcode; + const char *message_name; + int args_count; +-} messages[] = { +- { +- .type = WL_PROTOCOL_LOGGER_REQUEST, +- .class = "wl_display", +- .opcode = 0, +- .message_name = "sync", +- .args_count = 1, +- }, +- { +- .type = WL_PROTOCOL_LOGGER_EVENT, +- .class = "wl_callback", +- .opcode = 0, +- .message_name = "done", +- .args_count = 1, +- }, +- { +- .type = WL_PROTOCOL_LOGGER_EVENT, +- .class = "wl_display", +- .opcode = 1, +- .message_name = "delete_id", +- .args_count = 1, +- }, + }; + ++struct client { ++ struct wl_display *display; ++ struct wl_callback *cb; ++ struct wl_client_observer *sequence_observer; ++ ++ struct expected_client_message *expected_msg; ++ int expected_msg_count; ++ int actual_msg_count; ++}; ++ ++#define ASSERT_LT(arg1, arg2, ...) \ ++ if (arg1 >= arg2) \ ++ fprintf(stderr, __VA_ARGS__); \ ++ assert(arg1 < arg2) ++ ++#define ASSERT_EQ(arg1, arg2, ...) \ ++ if (arg1 != arg2) \ ++ fprintf(stderr, __VA_ARGS__); \ ++ assert(arg1 == arg2) ++ ++#define ASSERT_STR_EQ(arg1, arg2, ...) \ ++ if (strcmp(arg1, arg2) != 0) \ ++ fprintf(stderr, __VA_ARGS__); \ ++ assert(strcmp(arg1, arg2) == 0) ++ + static void +-logger_func(void *user_data, enum wl_protocol_logger_type type, +- const struct wl_protocol_logger_message *message) ++compositor_sequence_observer_func( ++ void *user_data, enum wl_protocol_logger_type actual_type, ++ const struct wl_protocol_logger_message *actual_msg) + { + struct compositor *c = user_data; +- struct message *msg = &messages[c->message++]; ++ struct expected_compositor_message *expected_msg; ++ int actual_msg_count = c->actual_msg_count++; ++ char details_msg[256]; ++ ++ c->client = wl_resource_get_client(actual_msg->resource); ++ ++ if (!c->expected_msg) ++ return; ++ ++ ASSERT_LT(actual_msg_count, c->expected_msg_count, ++ "actual count %d exceeds expected count %d\n", ++ actual_msg_count, c->expected_msg_count); ++ ++ expected_msg = &c->expected_msg[actual_msg_count]; ++ ++ snprintf(details_msg, sizeof details_msg, ++ "compositor msg %d of %d actual [%d, '%s', %d, '%s', %d] vs " ++ "expected [%d, '%s', %d, '%s', %d]\n", ++ c->actual_msg_count, c->expected_msg_count, actual_type, ++ wl_resource_get_class(actual_msg->resource), ++ actual_msg->message_opcode, actual_msg->message->name, ++ actual_msg->arguments_count, expected_msg->type, ++ expected_msg->class, expected_msg->opcode, ++ expected_msg->message_name, expected_msg->args_count); + +- assert(msg->type == type); +- assert(strcmp(msg->class, wl_resource_get_class(message->resource)) == 0); +- assert(msg->opcode == message->message_opcode); +- assert(strcmp(msg->message_name, message->message->name) == 0); +- assert(msg->args_count == message->arguments_count); ++ ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s", ++ details_msg); ++ ASSERT_STR_EQ(expected_msg->class, ++ wl_resource_get_class(actual_msg->resource), ++ "class mismatch: %s", details_msg); ++ ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode, ++ "opcode mismatch: %s", details_msg); ++ ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name, ++ "message name mismatch: %s", details_msg); ++ ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count, ++ "arg count mismatch: %s", details_msg); ++} ++ ++static void ++client_sequence_observer_func( ++ void *user_data, enum wl_client_message_type actual_type, ++ const struct wl_client_observed_message *actual_msg) ++{ ++ struct client *c = user_data; ++ struct expected_client_message *expected_msg; ++ int actual_msg_count = c->actual_msg_count++; ++ char details_msg[256]; ++ ++ if (!c->expected_msg) ++ return; ++ ++ ASSERT_LT(actual_msg_count, c->expected_msg_count, ++ "actual count %d exceeds expected count %d\n", ++ actual_msg_count, c->expected_msg_count); ++ expected_msg = &c->expected_msg[actual_msg_count]; + +- c->client = wl_resource_get_client(message->resource); ++ snprintf(details_msg, sizeof details_msg, ++ "client msg %d of %d actual [%d, %d, '%s', %d, '%s', %d] vs " ++ "expected [%d, %d, '%s', %d, '%s', %d]\n", ++ c->actual_msg_count, c->expected_msg_count, actual_type, ++ actual_msg->discarded_reason, ++ wl_proxy_get_class(actual_msg->proxy), ++ actual_msg->message_opcode, actual_msg->message->name, ++ actual_msg->arguments_count, expected_msg->type, ++ expected_msg->discarded_reason, expected_msg->class, ++ expected_msg->opcode, expected_msg->message_name, ++ expected_msg->args_count); ++ ++ ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s", ++ details_msg); ++ ASSERT_EQ(expected_msg->discarded_reason, actual_msg->discarded_reason, ++ "discarded reason mismatch: %s", details_msg); ++ ASSERT_STR_EQ(expected_msg->class, ++ wl_proxy_get_class(actual_msg->proxy), ++ "class mismatch: %s", details_msg); ++ ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode, ++ "opcode mismatch: %s", details_msg); ++ ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name, ++ "message name mismatch: %s", details_msg); ++ ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count, ++ "arg count mismatch: %s", details_msg); + } + + static void +@@ -108,41 +200,236 @@ static const struct wl_callback_listener callback_listener = { + callback_done, + }; + ++static void ++logger_setup(struct compositor *compositor, struct client *client) ++{ ++ const char *socket; ++ ++ require_xdg_runtime_dir(); ++ ++ compositor->display = wl_display_create(); ++ compositor->loop = wl_display_get_event_loop(compositor->display); ++ socket = wl_display_add_socket_auto(compositor->display); ++ ++ compositor->logger = wl_display_add_protocol_logger( ++ compositor->display, compositor_sequence_observer_func, ++ compositor); ++ ++ client->display = wl_display_connect(socket); ++ client->sequence_observer = wl_display_create_client_observer( ++ client->display, client_sequence_observer_func, client); ++} ++ ++static void ++logger_teardown(struct compositor *compositor, struct client *client) ++{ ++ wl_client_observer_destroy(client->sequence_observer); ++ wl_display_disconnect(client->display); ++ ++ wl_client_destroy(compositor->client); ++ wl_protocol_logger_destroy(compositor->logger); ++ wl_display_destroy(compositor->display); ++} ++ + TEST(logger) + { + test_set_timeout(1); + +- const char *socket; ++ struct expected_compositor_message compositor_messages[] = { ++ { ++ .type = WL_PROTOCOL_LOGGER_REQUEST, ++ .class = "wl_display", ++ .opcode = 0, ++ .message_name = "sync", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_PROTOCOL_LOGGER_EVENT, ++ .class = "wl_callback", ++ .opcode = 0, ++ .message_name = "done", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_PROTOCOL_LOGGER_EVENT, ++ .class = "wl_display", ++ .opcode = 1, ++ .message_name = "delete_id", ++ .args_count = 1, ++ }, ++ }; ++ struct expected_client_message client_messages[] = { ++ { ++ .type = WL_CLIENT_MESSAGE_REQUEST, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_display", ++ .opcode = 0, ++ .message_name = "sync", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_CLIENT_MESSAGE_EVENT, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_display", ++ .opcode = 1, ++ .message_name = "delete_id", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_CLIENT_MESSAGE_EVENT, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_callback", ++ .opcode = 0, ++ .message_name = "done", ++ .args_count = 1, ++ }, ++ }; + struct compositor compositor = { 0 }; +- struct { +- struct wl_display *display; +- struct wl_callback *cb; +- } client; +- struct wl_protocol_logger *logger; ++ struct client client = { 0 }; + +- require_xdg_runtime_dir(); ++ logger_setup(&compositor, &client); + +- compositor.display = wl_display_create(); +- compositor.loop = wl_display_get_event_loop(compositor.display); +- socket = wl_display_add_socket_auto(compositor.display); ++ compositor.expected_msg = &compositor_messages[0]; ++ compositor.expected_msg_count = ARRAY_LENGTH(compositor_messages); + +- logger = wl_display_add_protocol_logger(compositor.display, +- logger_func, &compositor); ++ client.expected_msg = &client_messages[0]; ++ client.expected_msg_count = ARRAY_LENGTH(client_messages); + +- client.display = wl_display_connect(socket); + client.cb = wl_display_sync(client.display); + wl_callback_add_listener(client.cb, &callback_listener, NULL); + wl_display_flush(client.display); + +- while (compositor.message < 3) { ++ while (compositor.actual_msg_count < compositor.expected_msg_count) { + wl_event_loop_dispatch(compositor.loop, -1); + wl_display_flush_clients(compositor.display); + } + +- wl_display_dispatch(client.display); +- wl_display_disconnect(client.display); ++ while (client.actual_msg_count < client.expected_msg_count) { ++ wl_display_dispatch(client.display); ++ } ++ ++ logger_teardown(&compositor, &client); ++} ++ ++TEST(client_discards_if_dead_on_dispatch) ++{ ++ test_set_timeout(1); ++ ++ struct expected_client_message client_messages[] = { ++ { ++ .type = WL_CLIENT_MESSAGE_REQUEST, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_display", ++ .opcode = 0, ++ .message_name = "sync", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_CLIENT_MESSAGE_EVENT, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_display", ++ .opcode = 1, ++ .message_name = "delete_id", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_CLIENT_MESSAGE_EVENT, ++ .discarded_reason = ++ WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, ++ .class = "wl_callback", ++ .opcode = 0, ++ .message_name = "done", ++ .args_count = 1, ++ }, ++ }; ++ struct compositor compositor = { 0 }; ++ struct client client = { 0 }; ++ ++ logger_setup(&compositor, &client); ++ ++ compositor.expected_msg_count = 3; ++ ++ client.expected_msg = &client_messages[0]; ++ client.expected_msg_count = ARRAY_LENGTH(client_messages); ++ ++ client.cb = wl_display_sync(client.display); ++ wl_callback_add_listener(client.cb, &callback_listener, NULL); ++ wl_display_flush(client.display); ++ ++ while (compositor.actual_msg_count < compositor.expected_msg_count) { ++ wl_event_loop_dispatch(compositor.loop, -1); ++ wl_display_flush_clients(compositor.display); ++ } ++ ++ wl_display_prepare_read(client.display); ++ wl_display_read_events(client.display); ++ ++ // To get a WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, we ++ // destroy the callback after reading client events, but before ++ // dispatching them. ++ wl_callback_destroy(client.cb); ++ ++ while (client.actual_msg_count < client.expected_msg_count) { ++ wl_display_dispatch(client.display); ++ } ++ ++ logger_teardown(&compositor, &client); ++} ++ ++TEST(client_discards_if_no_listener_on_dispatch) ++{ ++ test_set_timeout(1); ++ ++ struct expected_client_message client_messages[] = { ++ { ++ .type = WL_CLIENT_MESSAGE_REQUEST, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_display", ++ .opcode = 0, ++ .message_name = "sync", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_CLIENT_MESSAGE_EVENT, ++ .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED, ++ .class = "wl_display", ++ .opcode = 1, ++ .message_name = "delete_id", ++ .args_count = 1, ++ }, ++ { ++ .type = WL_CLIENT_MESSAGE_EVENT, ++ .discarded_reason = ++ WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH, ++ .class = "wl_callback", ++ .opcode = 0, ++ .message_name = "done", ++ .args_count = 1, ++ }, ++ }; ++ struct compositor compositor = { 0 }; ++ struct client client = { 0 }; ++ ++ logger_setup(&compositor, &client); ++ ++ compositor.expected_msg_count = 3; ++ ++ client.expected_msg = &client_messages[0]; ++ client.expected_msg_count = ARRAY_LENGTH(client_messages); ++ ++ client.cb = wl_display_sync(client.display); ++ wl_display_flush(client.display); ++ ++ while (compositor.actual_msg_count < compositor.expected_msg_count) { ++ wl_event_loop_dispatch(compositor.loop, -1); ++ wl_display_flush_clients(compositor.display); ++ } ++ ++ while (client.actual_msg_count < client.expected_msg_count) { ++ wl_display_dispatch(client.display); ++ } ++ ++ wl_callback_destroy(client.cb); + +- wl_client_destroy(compositor.client); +- wl_protocol_logger_destroy(logger); +- wl_display_destroy(compositor.display); ++ logger_teardown(&compositor, &client); + } |