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>
This commit is contained in:
Lloyd Pique 2022-03-10 17:44:32 -08:00
parent 66be76c169
commit ed016b240f
3 changed files with 625 additions and 88 deletions

View file

@ -302,6 +302,110 @@ void
wl_display_set_max_buffer_size(struct wl_display *display,
size_t max_buffer_size);
/**
* 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 queue name, or NULL if the event is not associated with a
* particular queue.
*/
const char *queue_name;
};
/**
* 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.
*
* Via the client observer interfaces, 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

View file

@ -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;
/**
@ -153,6 +162,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.
@ -160,16 +191,22 @@ 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
* \param queue_name name for the queue for the message
*
*/
static void
closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send,
bool discarded, const char *queue_name)
enum wl_client_message_discarded_reason discarded_reason, const char *queue_name)
{
struct wl_display *display = proxy->display;
const char *discarded_reason_str;
struct wl_closure adjusted_closure = { 0 };
if (!debug_client && wl_list_empty(&display->observers))
return;
// Note: The real closure has extra data (referenced by its args
// immediately following the structure in memory, but we don't
// need to duplicate that.
@ -178,8 +215,31 @@ 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, queue_name);
discarded_reason_str = get_discarded_reason_str(discarded_reason);
if (debug_client)
wl_closure_print(&adjusted_closure, &proxy->object, send,
discarded_reason_str, queue_name);
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;
message.queue_name = queue_name;
wl_list_for_each(observer, &display->observers, link) {
observer->func(observer->user_data, type, &message);
}
}
}
/**
@ -965,6 +1025,8 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode,
struct wl_proxy *new_proxy = NULL;
const struct wl_message *message;
struct wl_display *disp = proxy->display;
struct wl_event_queue *queue = NULL;
const char *queue_name = NULL;
pthread_mutex_lock(&disp->mutex);
@ -990,18 +1052,12 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode,
goto err_unlock;
}
if (debug_client) {
struct wl_event_queue *queue;
const char *queue_name = NULL;
queue = wl_proxy_get_queue(proxy);
if (queue)
queue_name = wl_event_queue_get_name(queue);
queue = wl_proxy_get_queue(proxy);
if (queue)
queue_name = wl_event_queue_get_name(queue);
closure_log(closure, proxy, true, false,
queue_name);
}
closure_log(closure, proxy, true, WL_CLIENT_MESSAGE_NOT_DISCARDED,
queue_name);
if (wl_closure_send(closure, proxy->display->connection)) {
wl_log("Error sending request for %s.%s: %s\n",
@ -1311,6 +1367,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;
@ -1442,6 +1499,7 @@ wl_display_disconnect(struct wl_display *display)
free(display->default_queue.name);
wl_event_queue_release(&display->display_queue);
free(display->display_queue.name);
wl_list_remove(&display->observers);
pthread_mutex_destroy(&display->mutex);
pthread_cond_destroy(&display->reader_cond);
close(display->fd);
@ -1714,30 +1772,32 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
proxy = closure->proxy;
proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED);
if (debug_client) {
bool discarded = proxy_destroyed ||
!(proxy->dispatcher || proxy->object.implementation);
closure_log(closure, proxy, false, discarded, queue->name);
}
if (proxy_destroyed) {
destroy_queued_closure(closure);
return;
}
closure_log(closure, proxy, false,
WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH,
queue->name);
} else if (proxy->dispatcher) {
closure_log(closure, proxy, false,
WL_CLIENT_MESSAGE_NOT_DISCARDED, queue->name);
pthread_mutex_unlock(&display->mutex);
if (proxy->dispatcher) {
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,
WL_CLIENT_MESSAGE_NOT_DISCARDED, queue->name);
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,
queue->name);
}
pthread_mutex_lock(&display->mutex);
destroy_queued_closure(closure);
}
@ -2665,3 +2725,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);
}

View file

@ -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,162 @@ require_xdg_runtime_dir(void)
return val;
}
struct compositor {
struct wl_display *display;
struct wl_event_loop *loop;
int message;
struct wl_client *client;
};
struct message {
struct expected_compositor_message {
enum wl_protocol_logger_type type;
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 compositor {
struct wl_display *display;
struct wl_event_loop *loop;
struct wl_protocol_logger *logger;
struct expected_compositor_message *expected_msg;
int expected_msg_count;
int actual_msg_count;
struct wl_client *client;
};
struct expected_client_message {
enum wl_client_message_type type;
enum wl_client_message_discarded_reason discarded_reason;
const char *queue_name;
const char *class;
int opcode;
const char *message_name;
int args_count;
};
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;
};
static int
safe_strcmp(const char *s1, const char *s2) {
if (s1 == NULL && s2 == NULL)
return 0;
if (s1 == NULL && s2 != NULL)
return 1;
if (s1 != NULL && s2 == NULL)
return -1;
return strcmp(s1, s2);
}
#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 (safe_strcmp(arg1, arg2) != 0) \
fprintf(stderr, __VA_ARGS__); \
assert(safe_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];
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);
c->client = wl_resource_get_client(actual_msg->resource);
c->client = wl_resource_get_client(message->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_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];
snprintf(details_msg, sizeof details_msg,
"client msg %d of %d actual [%d, %d, '%s', '%s', %d, '%s', %d] vs "
"expected [%d, %d, '%s', '%s', %d, '%s', %d]\n",
c->actual_msg_count, c->expected_msg_count, actual_type,
actual_msg->discarded_reason,
actual_msg->queue_name ? actual_msg->queue_name : "NULL",
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->queue_name ? expected_msg->queue_name : "NULL",
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->queue_name, actual_msg->queue_name,
"queue name 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 +216,245 @@ 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,
.queue_name = "Default Queue",
.class = "wl_display",
.opcode = 0,
.message_name = "sync",
.args_count = 1,
},
{
.type = WL_CLIENT_MESSAGE_EVENT,
.discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
.queue_name = "Display Queue",
.class = "wl_display",
.opcode = 1,
.message_name = "delete_id",
.args_count = 1,
},
{
.type = WL_CLIENT_MESSAGE_EVENT,
.discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
.queue_name = "Default Queue",
.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);
}
wl_client_destroy(compositor.client);
wl_protocol_logger_destroy(logger);
wl_display_destroy(compositor.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,
.queue_name = "Default Queue",
.class = "wl_display",
.opcode = 0,
.message_name = "sync",
.args_count = 1,
},
{
.type = WL_CLIENT_MESSAGE_EVENT,
.discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
.queue_name = "Display Queue",
.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,
.queue_name = "Default Queue",
.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,
.queue_name = "Default Queue",
.class = "wl_display",
.opcode = 0,
.message_name = "sync",
.args_count = 1,
},
{
.type = WL_CLIENT_MESSAGE_EVENT,
.discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
.queue_name = "Display Queue",
.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,
.queue_name = "Default Queue",
.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);
logger_teardown(&compositor, &client);
}