From daa80916665f30cfce08c8f64c38b25f5ac4eb3b Mon Sep 17 00:00:00 2001 From: Julian Bouzas Date: Mon, 16 Mar 2020 15:11:55 -0400 Subject: [PATCH] modules: add monitor dbus device reservation API with unit tests --- meson.build | 1 + modules/meson.build | 10 +- .../module-monitor/dbus-device-reservation.c | 521 ++++++++++++++++++ .../module-monitor/dbus-device-reservation.h | 49 ++ .../org.freedesktop.ReserveDevice1.xml | 11 + tests/modules/dbus-device-reservation.c | 202 +++++++ tests/modules/meson.build | 14 + 7 files changed, 807 insertions(+), 1 deletion(-) create mode 100644 modules/module-monitor/dbus-device-reservation.c create mode 100644 modules/module-monitor/dbus-device-reservation.h create mode 100644 modules/module-monitor/org.freedesktop.ReserveDevice1.xml create mode 100644 tests/modules/dbus-device-reservation.c diff --git a/meson.build b/meson.build index 97c5caa0..29b8077c 100644 --- a/meson.build +++ b/meson.build @@ -31,6 +31,7 @@ cpptoml_dep = cpptoml.dependency('cpptoml') gobject_dep = dependency('gobject-2.0') gmodule_dep = dependency('gmodule-2.0') gio_dep = dependency('gio-2.0') +giounix_dep = dependency('gio-unix-2.0') pipewire_dep = dependency('libpipewire-0.3') gnome = import('gnome') diff --git a/modules/meson.build b/modules/meson.build index 385f5e3a..e12e01ba 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -14,15 +14,23 @@ shared_library( dependencies : [wp_dep, pipewire_dep], ) +reserve_device_interface_src = gnome.gdbus_codegen('reserve-device-interface', + sources: 'module-monitor/org.freedesktop.ReserveDevice1.xml', + interface_prefix : 'org.freedesktop.ReserveDevice1.', + namespace : 'WpMonitor' +) + shared_library( 'wireplumber-module-monitor', [ 'module-monitor.c', + 'module-monitor/dbus-device-reservation.c', + reserve_device_interface_src, ], c_args : [common_c_args, '-DG_LOG_DOMAIN="m-monitor"'], install : true, install_dir : wireplumber_module_dir, - dependencies : [wp_dep, pipewire_dep], + dependencies : [wp_dep, pipewire_dep, giounix_dep], ) shared_library( diff --git a/modules/module-monitor/dbus-device-reservation.c b/modules/module-monitor/dbus-device-reservation.c new file mode 100644 index 00000000..f9164ec8 --- /dev/null +++ b/modules/module-monitor/dbus-device-reservation.c @@ -0,0 +1,521 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +/* Generated with gdbus-codegen */ +#include "reserve-device-interface.h" + +#include + +#include "dbus-device-reservation.h" + +#define DEVICE_RESERVATION_SERVICE_PREFIX "org.freedesktop.ReserveDevice1." +#define DEVICE_RESERVATION_OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/" + +struct _WpMonitorDbusDeviceReservation +{ + GObject parent; + + /* Props */ + gint card_id; + char *application_name; + gint priority; + char *app_dev_name; + + char *service_name; + char *object_path; + GDBusConnection *connection; + guint owner_id; + guint registered_id; + GDBusMethodInvocation *pending_release; + + GTask *pending_task; + char *pending_property_name; +}; + +enum { + PROP_0, + PROP_CARD_ID, + PROP_APPLICATION_NAME, + PROP_PRIORITY, + PROP_APP_DEV_NAME, +}; + +enum +{ + SIGNAL_RELEASE, + SIGNAL_LAST, +}; + +static guint device_reservation_signals[SIGNAL_LAST] = { 0 }; + +G_DEFINE_TYPE (WpMonitorDbusDeviceReservation, + wp_monitor_dbus_device_reservation, G_TYPE_OBJECT) + +static void +handle_method_call (GDBusConnection *connection, const char *sender, + const char *object_path, const char *interface_name, + const char *method_name, GVariant *parameters, + GDBusMethodInvocation *invocation, gpointer data) +{ + WpMonitorDbusDeviceReservation *self = data; + + if (g_strcmp0 (method_name, "RequestRelease") == 0) { + gint priority; + g_variant_get (parameters, "(i)", &priority); + + if (priority > self->priority) { + if (self->pending_release) + wp_monitor_dbus_device_reservation_complete_release (self, FALSE); + self->pending_release = g_object_ref (invocation); + g_signal_emit (self, device_reservation_signals[SIGNAL_RELEASE], 0, 0); + } else { + wp_monitor_dbus_device_reservation_complete_release (self, FALSE); + } + } +} + +static GVariant * +handle_get_property (GDBusConnection *connection, const char *sender, + const char *object_path, const char *interface_name, + const char *property, GError **error, gpointer data) +{ + WpMonitorDbusDeviceReservation *self = data; + GVariant *ret = NULL; + + if (g_strcmp0 (property, "ApplicationName") == 0) + ret = g_variant_new_string (self->application_name ? self->application_name : ""); + else if (g_strcmp0 (property, "ApplicationDeviceName") == 0) + ret = g_variant_new_string (self->app_dev_name ? self->app_dev_name : ""); + else if (g_strcmp0 (property, "Priority") == 0) + ret = g_variant_new_int32 (self->priority); + + return ret; +} + +static void +on_bus_acquired (GDBusConnection *connection, const gchar *name, + gpointer user_data) +{ + WpMonitorDbusDeviceReservation *self = user_data; + g_autoptr (GError) error = NULL; + static const GDBusInterfaceVTable interface_vtable = { + handle_method_call, + handle_get_property, + NULL, /* Don't allow setting a property */ + }; + + g_debug ("WpMonitorDbusDeviceReservation:%p bus acquired", self); + + self->registered_id = g_dbus_connection_register_object (connection, + self->object_path, + wp_monitor_org_freedesktop_reserve_device1_interface_info (), + &interface_vtable, + g_object_ref (self), + g_object_unref, + &error); + g_return_if_fail (!error); + g_return_if_fail (self->registered_id > 0); +} + +static void +on_name_acquired (GDBusConnection *connection, const gchar *name, + gpointer user_data) +{ + WpMonitorDbusDeviceReservation *self = user_data; + g_debug ("WpMonitorDbusDeviceReservation:%p name acquired", self); + + self->connection = connection; + + /* Trigger the acquired task */ + if (self->pending_task) { + g_task_return_pointer (self->pending_task, GUINT_TO_POINTER (TRUE), NULL); + g_clear_object (&self->pending_task); + } +} + +static void +wp_monitor_dbus_device_reservation_unregister_object ( + WpMonitorDbusDeviceReservation *self) +{ + if (self->connection && self->registered_id > 0) { + g_dbus_connection_unregister_object (self->connection, self->registered_id); + self->registered_id = 0; + self->connection = NULL; + } +} + +static void +on_name_lost (GDBusConnection *connection, const gchar *name, + gpointer user_data) +{ + WpMonitorDbusDeviceReservation *self = user_data; + g_debug ("WpMonitorDbusDeviceReservation:%p name lost\n", self); + + self->connection = connection; + + /* Unregister object */ + wp_monitor_dbus_device_reservation_unregister_object (self); + + /* Trigger the acquired task */ + if (self->pending_task) { + GError *error = g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, + "dbus name lost before acquiring (connection=%p)", connection); + g_task_return_error (self->pending_task, error); + g_clear_object (&self->pending_task); + } +} + +static void +wp_monitor_dbus_device_reservation_constructed (GObject * object) +{ + WpMonitorDbusDeviceReservation *self = + WP_MONITOR_DBUS_DEVICE_RESERVATION (object); + + /* Set service name and object path */ + self->service_name = g_strdup_printf (DEVICE_RESERVATION_SERVICE_PREFIX + "Audio%d", self->card_id); + self->object_path = g_strdup_printf (DEVICE_RESERVATION_OBJECT_PREFIX + "Audio%d", self->card_id); + + G_OBJECT_CLASS ( + wp_monitor_dbus_device_reservation_parent_class)->constructed (object); +} + +static void +wp_monitor_dbus_device_reservation_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + WpMonitorDbusDeviceReservation *self = + WP_MONITOR_DBUS_DEVICE_RESERVATION (object); + + switch (property_id) { + case PROP_CARD_ID: + g_value_set_int (value, self->card_id); + break; + case PROP_APPLICATION_NAME: + g_value_set_string (value, self->application_name); + break; + case PROP_PRIORITY: + g_value_set_int (value, self->priority); + break; + case PROP_APP_DEV_NAME: + g_value_set_string (value, self->app_dev_name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_monitor_dbus_device_reservation_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec) +{ + WpMonitorDbusDeviceReservation *self = + WP_MONITOR_DBUS_DEVICE_RESERVATION (object); + + switch (property_id) { + case PROP_CARD_ID: + self->card_id = g_value_get_int (value); + break; + case PROP_APPLICATION_NAME: + g_clear_pointer (&self->application_name, g_free); + self->application_name = g_value_dup_string (value); + break; + case PROP_PRIORITY: + self->priority = g_value_get_int(value); + break; + case PROP_APP_DEV_NAME: + g_clear_pointer (&self->app_dev_name, g_free); + self->app_dev_name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_monitor_dbus_device_reservation_finalize (GObject * object) +{ + WpMonitorDbusDeviceReservation *self = + WP_MONITOR_DBUS_DEVICE_RESERVATION (object); + + /* Finish pending task */ + if (self->pending_task) { + GError *error = g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, "finishing before task is done"); + g_task_return_error (self->pending_task, error); + } + g_clear_object (&self->pending_task); + g_clear_pointer (&self->pending_property_name, g_free); + + /* Unregister and release */ + wp_monitor_dbus_device_reservation_unregister_object (self); + wp_monitor_dbus_device_reservation_release (self); + + g_clear_object (&self->pending_release); + g_clear_pointer (&self->service_name, g_free); + g_clear_pointer (&self->object_path, g_free); + + /* Props */ + g_clear_pointer (&self->application_name, g_free); + g_clear_pointer (&self->app_dev_name, g_free); + + G_OBJECT_CLASS ( + wp_monitor_dbus_device_reservation_parent_class)->finalize (object); +} + +static void +wp_monitor_dbus_device_reservation_init (WpMonitorDbusDeviceReservation * self) +{ +} + +static void +wp_monitor_dbus_device_reservation_class_init ( + WpMonitorDbusDeviceReservationClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->constructed = wp_monitor_dbus_device_reservation_constructed; + object_class->get_property = wp_monitor_dbus_device_reservation_get_property; + object_class->set_property = wp_monitor_dbus_device_reservation_set_property; + object_class->finalize = wp_monitor_dbus_device_reservation_finalize; + + /* Properties */ + g_object_class_install_property (object_class, PROP_CARD_ID, + g_param_spec_int ("card-id", "card-id", + "The card Id", G_MININT, G_MAXINT, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_APPLICATION_NAME, + g_param_spec_string ("application-name", "application-name", + "The application name", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_PRIORITY, + g_param_spec_int ("priority", "priority", + "The priority", G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_APP_DEV_NAME, + g_param_spec_string ("app-dev-name", "app-dev-name", + "The application device name", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + /* Signals */ + device_reservation_signals[SIGNAL_RELEASE] = g_signal_new ( + "release", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT); +} + +WpMonitorDbusDeviceReservation * +wp_monitor_dbus_device_reservation_new (gint card_id, + const char *application_name, gint priority, const char *app_dev_name) +{ + return g_object_new (WP_TYPE_MONITOR_DBUS_DEVICE_RESERVATION, + "card-id", card_id, + "application-name", application_name, + "priority", priority, + "app-dev-name", app_dev_name, + NULL); +} + +void +wp_monitor_dbus_device_reservation_release ( + WpMonitorDbusDeviceReservation *self) +{ + g_return_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self)); + if (self->owner_id == 0) + return; + + /* Release */ + g_bus_unown_name (self->owner_id); + self->owner_id = 0; +} + +void +wp_monitor_dbus_device_reservation_complete_release ( + WpMonitorDbusDeviceReservation *self, gboolean res) +{ + g_return_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self)); + + if (!self->pending_release) + return; + + g_dbus_method_invocation_return_value (self->pending_release, + g_variant_new ("(b)", g_variant_new_boolean (res))); + g_clear_object (&self->pending_release); +} + +static void +on_unowned (gpointer user_data) +{ + WpMonitorDbusDeviceReservation *self = user_data; + wp_monitor_dbus_device_reservation_unregister_object (self); + g_object_unref (self); +} + +gboolean +wp_monitor_dbus_device_reservation_acquire ( + WpMonitorDbusDeviceReservation *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE); + g_return_val_if_fail (!self->pending_task, FALSE); + if (self->owner_id > 0) + return FALSE; + + /* Set the new task */ + self->pending_task = g_task_new (self, cancellable, callback, user_data); + + /* Aquire */ + self->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, self->service_name, + self->priority < INT32_MAX ? + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT : G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, on_name_acquired, on_name_lost, g_object_ref (self), + on_unowned); + g_return_val_if_fail (self->owner_id > 0, FALSE); + return TRUE; +} + +static void +on_request_release_done (GObject *proxy, GAsyncResult *res, gpointer user_data) +{ + WpMonitorDbusDeviceReservation *self = user_data; + g_autoptr (GError) error = NULL; + gboolean ret; + + /* Finish */ + wp_monitor_org_freedesktop_reserve_device1_call_request_release_finish ( + WP_MONITOR_ORG_FREEDESKTOP_RESERVE_DEVICE1 (proxy), + &ret, res, &error); + + /* Return */ + g_return_if_fail (self->pending_task); + if (error) + g_task_return_error (self->pending_task, g_steal_pointer (&error)); + else + g_task_return_pointer (self->pending_task, GUINT_TO_POINTER (ret), NULL); + g_clear_object (&self->pending_task); +} + +static void +on_proxy_done_request_release (GObject *obj, GAsyncResult *res, gpointer data) +{ + WpMonitorDbusDeviceReservation *self = data; + g_autoptr (WpMonitorOrgFreedesktopReserveDevice1) proxy = NULL; + g_autoptr (GError) error = NULL; + + /* Finish */ + proxy = wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus_finish ( + res, &error); + + /* Check for errors */ + g_return_if_fail (self->pending_task); + if (error) { + g_task_return_error (self->pending_task, g_steal_pointer (&error)); + g_clear_object (&self->pending_task); + return; + } + + /* Request release */ + g_return_if_fail (proxy); + wp_monitor_org_freedesktop_reserve_device1_call_request_release (proxy, + self->priority, NULL, on_request_release_done, self); +} + +gboolean +wp_monitor_dbus_device_reservation_request_release ( + WpMonitorDbusDeviceReservation *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) +{ + g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE); + g_return_val_if_fail (!self->pending_task, FALSE); + + /* Set the new task */ + self->pending_task = g_task_new (self, cancellable, callback, user_data); + + /* Get the proxy */ + wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus ( + G_BUS_TYPE_SESSION, G_BUS_NAME_OWNER_FLAGS_NONE, self->service_name, + self->object_path, NULL, on_proxy_done_request_release, self); + return TRUE; +} + +static void +on_proxy_done_request_property (GObject *obj, GAsyncResult *res, gpointer data) +{ + WpMonitorDbusDeviceReservation *self = data; + g_autoptr (WpMonitorOrgFreedesktopReserveDevice1) proxy = NULL; + g_autoptr (GError) error = NULL; + + /* Finish */ + proxy = wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus_finish ( + res, &error); + + /* Check for errors */ + g_return_if_fail (self->pending_task); + if (error) { + g_task_return_error (self->pending_task, g_steal_pointer (&error)); + g_clear_object (&self->pending_task); + return; + } + + /* Request the property */ + g_return_if_fail (proxy); + g_return_if_fail (self->pending_property_name); + + if (g_strcmp0 (self->pending_property_name, "ApplicationName") == 0) { + char *v = wp_monitor_org_freedesktop_reserve_device1_dup_application_name (proxy); + g_task_return_pointer (self->pending_task, v, g_free); + } + else if (g_strcmp0 (self->pending_property_name, "ApplicationDeviceName") == 0) { + char *v = wp_monitor_org_freedesktop_reserve_device1_dup_application_device_name (proxy); + g_task_return_pointer (self->pending_task, v, g_free); + } + else if (g_strcmp0 (self->pending_property_name, "Priority") == 0) { + gint v = wp_monitor_org_freedesktop_reserve_device1_get_priority (proxy); + g_task_return_pointer (self->pending_task, GINT_TO_POINTER (v), NULL); + } + else { + GError *error = g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, "invalid property '%s' on proxy", + self->pending_property_name); + g_task_return_error (self->pending_task, error); + } + g_clear_object (&self->pending_task); +} + +gboolean +wp_monitor_dbus_device_reservation_request_property ( + WpMonitorDbusDeviceReservation *self, const char *name, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) +{ + g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE); + g_return_val_if_fail (!self->pending_task, FALSE); + + /* Set the new task and property name */ + self->pending_task = g_task_new (self, cancellable, callback, user_data); + g_clear_pointer (&self->pending_property_name, g_free); + self->pending_property_name = g_strdup (name); + + /* Get the proxy */ + wp_monitor_org_freedesktop_reserve_device1_proxy_new_for_bus ( + G_BUS_TYPE_SESSION, G_BUS_NAME_OWNER_FLAGS_NONE, self->service_name, + self->object_path, NULL, on_proxy_done_request_property, self); + return TRUE; +} + +gpointer +wp_monitor_dbus_device_reservation_async_finish ( + WpMonitorDbusDeviceReservation *self, GAsyncResult * res, GError ** error) +{ + g_return_val_if_fail (WP_IS_MONITOR_DBUS_DEVICE_RESERVATION (self), FALSE); + g_return_val_if_fail (g_task_is_valid (res, self), FALSE); + + return g_task_propagate_pointer (G_TASK (res), error); +} diff --git a/modules/module-monitor/dbus-device-reservation.h b/modules/module-monitor/dbus-device-reservation.h new file mode 100644 index 00000000..5c9e28c6 --- /dev/null +++ b/modules/module-monitor/dbus-device-reservation.h @@ -0,0 +1,49 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_MONITOR_DBUS_DEVICE_RESERVATION_H__ +#define __WIREPLUMBER_MONITOR_DBUS_DEVICE_RESERVATION_H__ + +#include + +G_BEGIN_DECLS + +#define WP_TYPE_MONITOR_DBUS_DEVICE_RESERVATION (wp_monitor_dbus_device_reservation_get_type ()) +G_DECLARE_FINAL_TYPE (WpMonitorDbusDeviceReservation, + wp_monitor_dbus_device_reservation, WP, MONITOR_DBUS_DEVICE_RESERVATION, + GObject) + +WpMonitorDbusDeviceReservation * wp_monitor_dbus_device_reservation_new ( + gint card_id, const char *application_name, gint priority, + const char *app_dev_name); + +void wp_monitor_dbus_device_reservation_release ( + WpMonitorDbusDeviceReservation *self); + +void wp_monitor_dbus_device_reservation_complete_release ( + WpMonitorDbusDeviceReservation *self, gboolean res); + +gboolean wp_monitor_dbus_device_reservation_acquire ( + WpMonitorDbusDeviceReservation *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean wp_monitor_dbus_device_reservation_request_release ( + WpMonitorDbusDeviceReservation *self, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data); + +gboolean wp_monitor_dbus_device_reservation_request_property ( + WpMonitorDbusDeviceReservation *self, const char *name, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data); + +gpointer wp_monitor_dbus_device_reservation_async_finish ( + WpMonitorDbusDeviceReservation *self, GAsyncResult * res, GError ** error); + +G_END_DECLS + +#endif diff --git a/modules/module-monitor/org.freedesktop.ReserveDevice1.xml b/modules/module-monitor/org.freedesktop.ReserveDevice1.xml new file mode 100644 index 00000000..03a45a94 --- /dev/null +++ b/modules/module-monitor/org.freedesktop.ReserveDevice1.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/modules/dbus-device-reservation.c b/tests/modules/dbus-device-reservation.c new file mode 100644 index 00000000..e9c1f303 --- /dev/null +++ b/tests/modules/dbus-device-reservation.c @@ -0,0 +1,202 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include "../../../modules/module-monitor/dbus-device-reservation.h" + +typedef struct { + GTestDBus *dbus_test; + GMainLoop *loop; + gboolean acquired; + gboolean released; + gpointer property; +} TestDbusFixture; + +static void +test_dbus_setup (TestDbusFixture *self, gconstpointer data) +{ + self->dbus_test = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_up (self->dbus_test); + self->loop = g_main_loop_new (NULL, FALSE); +} + +static void +test_dbus_teardown (TestDbusFixture *self, gconstpointer data) +{ + g_clear_pointer (&self->loop, g_main_loop_unref); + g_test_dbus_down (self->dbus_test); + g_clear_object (&self->dbus_test); +} + +static void +on_reservation_release (WpMonitorDbusDeviceReservation *reservation, + int forced, TestDbusFixture *self) +{ + wp_monitor_dbus_device_reservation_release (reservation); + wp_monitor_dbus_device_reservation_complete_release (reservation, TRUE); +} + +static void +on_acquired_done (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + TestDbusFixture *self = user_data; + WpMonitorDbusDeviceReservation *r = WP_MONITOR_DBUS_DEVICE_RESERVATION (obj); + g_autoptr (GError) e = NULL; + wp_monitor_dbus_device_reservation_async_finish (r, res, &e); + g_assert_null (e); + self->acquired = TRUE; + g_main_loop_quit (self->loop); +} + +static void +on_request_release_done (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + TestDbusFixture *self = user_data; + WpMonitorDbusDeviceReservation *r = WP_MONITOR_DBUS_DEVICE_RESERVATION (obj); + g_autoptr (GError) e = NULL; + wp_monitor_dbus_device_reservation_async_finish (r, res, &e); + g_assert_null (e); + self->released = TRUE; + g_main_loop_quit (self->loop); +} + +static void +on_request_property_done (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + TestDbusFixture *self = user_data; + WpMonitorDbusDeviceReservation *r = WP_MONITOR_DBUS_DEVICE_RESERVATION (obj); + g_autoptr (GError) e = NULL; + gpointer ret = wp_monitor_dbus_device_reservation_async_finish (r, res, &e); + g_assert_null (e); + self->property = ret; + g_main_loop_quit (self->loop); +} + +static WpMonitorDbusDeviceReservation * +create_representation (TestDbusFixture *self, gint card_id, + const char *app_name, gint priority, const char *app_dev_name) +{ + WpMonitorDbusDeviceReservation *r = wp_monitor_dbus_device_reservation_new ( + card_id, app_name, priority, app_dev_name); + g_assert_nonnull (r); + g_signal_connect (r, "release", + (GCallback) on_reservation_release, self); + return r; +} + +static void +test_dbus_basic (TestDbusFixture *self, gconstpointer data) +{ + g_autoptr (WpMonitorDbusDeviceReservation) r1 = NULL; + g_autoptr (WpMonitorDbusDeviceReservation) r2 = NULL; + + /* Create 2 reservations */ + r1 = create_representation (self, 0, "Server", 10, "hw:0,0"); + r2 = create_representation (self, 0, "PipeWire", 15, "hw:0,0"); + + /* Acquire the device on r1 */ + self->acquired = FALSE; + g_assert_true (wp_monitor_dbus_device_reservation_acquire (r1, NULL, + on_acquired_done, self)); + g_main_loop_run (self->loop); + g_assert_true (self->acquired); + + /* Request the priority property on r1 and make sure it is 10 */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r1, + "Priority", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpint (GPOINTER_TO_INT (self->property), ==, 10); + + /* Request the application name property on r1 and make sure it is Server */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r1, + "ApplicationName", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpstr (self->property, ==, "Server"); + + /* Request the app device name property on r1 and make sure it is hw:0,0 */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r1, + "ApplicationDeviceName", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpstr (self->property, ==, "hw:0,0"); + + /* Request the priority property on r2 and make sure it is also 10 because r1 + * owns the device */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r2, + "Priority", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpint (GPOINTER_TO_INT (self->property), ==, 10); + + /* Request release on r2 (higher priority) */ + self->released = FALSE; + g_assert_true (wp_monitor_dbus_device_reservation_request_release (r2, NULL, + on_request_release_done, self)); + g_main_loop_run (self->loop); + g_assert_true (self->released); + + /* Acquire the device on r2 */ + self->acquired = FALSE; + g_assert_true (wp_monitor_dbus_device_reservation_acquire (r2, NULL, + on_acquired_done, self)); + g_main_loop_run (self->loop); + g_assert_true (self->acquired); + + /* Request the priority property on r2 and make sure it is 15 */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r2, + "Priority", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpint (GPOINTER_TO_INT (self->property), ==, 15); + + /* Request the application name property on r1 and make sure it is Server */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r2, + "ApplicationName", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpstr (self->property, ==, "PipeWire"); + + /* Request the app device name property on r2 and make sure it is hw:0,0 */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r2, + "ApplicationDeviceName", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpstr (self->property, ==, "hw:0,0"); + + /* Request the priority property on r1 and make sure it is also 15 because r2 + * owns the device now */ + self->property = NULL; + g_assert_true (wp_monitor_dbus_device_reservation_request_property (r1, + "Priority", NULL, on_request_property_done, self)); + g_main_loop_run (self->loop); + g_assert_nonnull (self->property); + g_assert_cmpint (GPOINTER_TO_INT (self->property), ==, 15); +} + +gint +main (gint argc, gchar *argv[]) +{ + g_test_init (&argc, &argv, NULL); + pw_init (NULL, NULL); + + g_test_add ("/wp/dbus/basic", TestDbusFixture, NULL, + test_dbus_setup, test_dbus_basic, test_dbus_teardown); + + return g_test_run (); +} diff --git a/tests/modules/meson.build b/tests/modules/meson.build index dcca073d..65db1736 100644 --- a/tests/modules/meson.build +++ b/tests/modules/meson.build @@ -58,3 +58,17 @@ test( env: common_env, workdir : meson.current_source_dir(), ) + +test( + 'test-dbus-device-reservation', + executable('test-dbus-device-reservation', + [ + 'dbus-device-reservation.c', + '../../modules/module-monitor/dbus-device-reservation.c', + reserve_device_interface_src, + ], + dependencies: common_deps + [giounix_dep]), + env: common_env, + workdir : meson.current_source_dir(), +) +