blob: ce3bec12ecf33b27ceb786bc48865d8f0e17af24 [file] [log] [blame]
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);
}