mirror of
https://github.com/openwrt/packages.git
synced 2025-12-23 16:44:31 +04:00
Bump glib2 to 2.74.7 which fixes CVE-2023-29499, CVE-2023-32611, CVE-2023-32636, CVE-2023-32643, CVE-2023-32665 and on top of that backport CVE-2024-34397 fix from Debian Bookworm glib2 package 2.74.6-2+deb12u2. While at it refresh the patches so they apply cleanly. References: https://security-tracker.debian.org/tracker/source-package/glib2.0 Fixes: CVE-2023-29499, CVE-2023-32611, CVE-2023-32636, CVE-2023-32643, CVE-2023-32665, CVE-2024-34397 Signed-off-by: Petr Štetiar <ynezz@true.cz>
974 lines
30 KiB
Diff
974 lines
30 KiB
Diff
From: Simon McVittie <smcv@collabora.com>
|
|
Date: Fri, 8 Mar 2024 14:19:46 +0000
|
|
Subject: tests: Add a data-driven test for signal subscriptions
|
|
|
|
This somewhat duplicates test_connection_signals(), but is easier to
|
|
extend to cover different scenarios.
|
|
|
|
Each scenario is tested three times: once with lower-level
|
|
GDBusConnection APIs, once with the higher-level GDBusProxy (which
|
|
cannot implement all of the subscription scenarios, so some message
|
|
counts are lower), and once with both (to check that delivery of the
|
|
same message to multiple destinations is handled appropriately).
|
|
|
|
[Backported to glib-2-74, resolving conflicts in gio/tests/meson.build]
|
|
Signed-off-by: Simon McVittie <smcv@collabora.com>
|
|
Origin: upstream, https://gitlab.gnome.org/GNOME/glib/-/issues/3268
|
|
---
|
|
gio/tests/gdbus-subscribe.c | 938 ++++++++++++++++++++++++++++++++++++++++++++
|
|
gio/tests/meson.build | 1 +
|
|
2 files changed, 939 insertions(+)
|
|
create mode 100644 gio/tests/gdbus-subscribe.c
|
|
|
|
--- /dev/null
|
|
+++ b/gio/tests/gdbus-subscribe.c
|
|
@@ -0,0 +1,938 @@
|
|
+/*
|
|
+ * Copyright 2024 Collabora Ltd.
|
|
+ * SPDX-License-Identifier: LGPL-2.1-or-later
|
|
+ */
|
|
+
|
|
+#include <gio/gio.h>
|
|
+
|
|
+#include "gdbus-tests.h"
|
|
+
|
|
+#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
|
|
+#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
|
|
+#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS
|
|
+
|
|
+/* A signal that each connection emits to indicate that it has finished
|
|
+ * emitting other signals */
|
|
+#define FINISHED_PATH "/org/gtk/Test/Finished"
|
|
+#define FINISHED_INTERFACE "org.gtk.Test.Finished"
|
|
+#define FINISHED_SIGNAL "Finished"
|
|
+
|
|
+/* A signal emitted during testing */
|
|
+#define EXAMPLE_PATH "/org/gtk/GDBus/ExampleInterface"
|
|
+#define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface"
|
|
+#define FOO_SIGNAL "Foo"
|
|
+
|
|
+/* Log @s in a debug message. */
|
|
+static inline const char *
|
|
+nonnull (const char *s,
|
|
+ const char *if_null)
|
|
+{
|
|
+ return (s == NULL) ? if_null : s;
|
|
+}
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ TEST_CONN_NONE,
|
|
+ TEST_CONN_FIRST,
|
|
+ /* A connection that subscribes to signals */
|
|
+ TEST_CONN_SUBSCRIBER = TEST_CONN_FIRST,
|
|
+ /* A mockup of a legitimate service */
|
|
+ TEST_CONN_SERVICE,
|
|
+ /* A mockup of a second legitimate service */
|
|
+ TEST_CONN_SERVICE2,
|
|
+ /* A connection that tries to trick @subscriber into processing its signals
|
|
+ * as if they came from @service */
|
|
+ TEST_CONN_ATTACKER,
|
|
+ NUM_TEST_CONNS
|
|
+} TestConn;
|
|
+
|
|
+static const char * const test_conn_descriptions[NUM_TEST_CONNS] =
|
|
+{
|
|
+ "(unused)",
|
|
+ "subscriber",
|
|
+ "service",
|
|
+ "service 2",
|
|
+ "attacker"
|
|
+};
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ SUBSCRIPTION_MODE_CONN,
|
|
+ SUBSCRIPTION_MODE_PROXY,
|
|
+ SUBSCRIPTION_MODE_PARALLEL
|
|
+} SubscriptionMode;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ GDBusProxy *received_by_proxy;
|
|
+ TestConn sender;
|
|
+ char *path;
|
|
+ char *iface;
|
|
+ char *member;
|
|
+ GVariant *parameters;
|
|
+ char *arg0;
|
|
+ guint32 step;
|
|
+} ReceivedMessage;
|
|
+
|
|
+static void
|
|
+received_message_free (ReceivedMessage *self)
|
|
+{
|
|
+
|
|
+ g_clear_object (&self->received_by_proxy);
|
|
+ g_free (self->path);
|
|
+ g_free (self->iface);
|
|
+ g_free (self->member);
|
|
+ g_clear_pointer (&self->parameters, g_variant_unref);
|
|
+ g_free (self->arg0);
|
|
+ g_free (self);
|
|
+}
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ TestConn sender;
|
|
+ TestConn unicast_to;
|
|
+ const char *path;
|
|
+ const char *iface;
|
|
+ const char *member;
|
|
+ const char *arg0;
|
|
+ guint received_by_conn;
|
|
+ guint received_by_proxy;
|
|
+} TestEmitSignal;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ TestConn sender;
|
|
+ const char *path;
|
|
+ const char *iface;
|
|
+ const char *member;
|
|
+ const char *arg0;
|
|
+ GDBusSignalFlags flags;
|
|
+} TestSubscribe;
|
|
+
|
|
+typedef enum
|
|
+{
|
|
+ TEST_ACTION_NONE = 0,
|
|
+ TEST_ACTION_SUBSCRIBE,
|
|
+ TEST_ACTION_EMIT_SIGNAL,
|
|
+} TestAction;
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ TestAction action;
|
|
+ union {
|
|
+ TestEmitSignal signal;
|
|
+ TestSubscribe subscribe;
|
|
+ } u;
|
|
+} TestStep;
|
|
+
|
|
+/* Arbitrary, extend as necessary to accommodate the longest test */
|
|
+#define MAX_TEST_STEPS 10
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ const char *description;
|
|
+ TestStep steps[MAX_TEST_STEPS];
|
|
+} TestPlan;
|
|
+
|
|
+static const TestPlan plan_simple =
|
|
+{
|
|
+ .description = "A broadcast is only received after subscribing to it",
|
|
+ .steps = {
|
|
+ {
|
|
+ /* We don't receive a signal if we haven't subscribed yet */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 0,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* Now it works */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 1,
|
|
+ /* The proxy can't be used in this case, because it needs
|
|
+ * a bus name to subscribe to */
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const TestPlan plan_broadcast_from_anyone =
|
|
+{
|
|
+ .description = "A subscription with NULL sender accepts broadcast and unicast",
|
|
+ .steps = {
|
|
+ {
|
|
+ /* Subscriber wants to receive signals from anyone */
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* First service sends a broadcast */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 1,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* Second service also sends a broadcast */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE2,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 1,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* First service sends a unicast signal */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .unicast_to = TEST_CONN_SUBSCRIBER,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 1,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* Second service also sends a unicast signal */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE2,
|
|
+ .unicast_to = TEST_CONN_SUBSCRIBER,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 1,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const TestPlan plan_match_twice =
|
|
+{
|
|
+ .description = "A message matching more than one subscription is received "
|
|
+ "once per subscription",
|
|
+ .steps = {
|
|
+ {
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .path = EXAMPLE_PATH,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 4,
|
|
+ /* Only the first and last work with GDBusProxy */
|
|
+ .received_by_proxy = 2
|
|
+ },
|
|
+ },
|
|
+ },
|
|
+};
|
|
+
|
|
+static const TestPlan plan_limit_by_unique_name =
|
|
+{
|
|
+ .description = "A subscription via a unique name only accepts messages "
|
|
+ "sent by that same unique name",
|
|
+ .steps = {
|
|
+ {
|
|
+ /* Subscriber wants to receive signals from service */
|
|
+ .action = TEST_ACTION_SUBSCRIBE,
|
|
+ .u.subscribe = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* Attacker wants to trick subscriber into thinking that service
|
|
+ * sent a signal */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_ATTACKER,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 0,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* Attacker tries harder, by sending a signal unicast directly to
|
|
+ * the subscriber */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_ATTACKER,
|
|
+ .unicast_to = TEST_CONN_SUBSCRIBER,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 0,
|
|
+ .received_by_proxy = 0
|
|
+ },
|
|
+ },
|
|
+ {
|
|
+ /* When the real service sends a signal, it should still get through */
|
|
+ .action = TEST_ACTION_EMIT_SIGNAL,
|
|
+ .u.signal = {
|
|
+ .sender = TEST_CONN_SERVICE,
|
|
+ .path = EXAMPLE_PATH,
|
|
+ .iface = EXAMPLE_INTERFACE,
|
|
+ .member = FOO_SIGNAL,
|
|
+ .received_by_conn = 1,
|
|
+ .received_by_proxy = 1
|
|
+ },
|
|
+ },
|
|
+ },
|
|
+};
|
|
+
|
|
+typedef struct
|
|
+{
|
|
+ const TestPlan *plan;
|
|
+ SubscriptionMode mode;
|
|
+ GError *error;
|
|
+ /* (element-type ReceivedMessage) */
|
|
+ GPtrArray *received;
|
|
+ /* conns[TEST_CONN_NONE] is unused and remains NULL */
|
|
+ GDBusConnection *conns[NUM_TEST_CONNS];
|
|
+ /* Proxies on conns[TEST_CONN_SUBSCRIBER] */
|
|
+ GPtrArray *proxies;
|
|
+ /* unique_names[TEST_CONN_NONE] is unused and remains NULL */
|
|
+ const char *unique_names[NUM_TEST_CONNS];
|
|
+ /* finished[TEST_CONN_NONE] is unused and remains FALSE */
|
|
+ gboolean finished[NUM_TEST_CONNS];
|
|
+ /* Remains 0 for any step that is not a subscription */
|
|
+ guint subscriptions[MAX_TEST_STEPS];
|
|
+ /* Number of times the signal from step n was received */
|
|
+ guint received_by_conn[MAX_TEST_STEPS];
|
|
+ /* Number of times the signal from step n was received */
|
|
+ guint received_by_proxy[MAX_TEST_STEPS];
|
|
+ guint finished_subscription;
|
|
+} Fixture;
|
|
+
|
|
+/* Wait for asynchronous messages from @conn to have been processed
|
|
+ * by the message bus, as a sequence point so that we can make
|
|
+ * "happens before" and "happens after" assertions relative to this.
|
|
+ * The easiest way to achieve this is to call a message bus method that has
|
|
+ * no arguments and wait for it to return: because the message bus processes
|
|
+ * messages in-order, anything we sent before this must have been processed
|
|
+ * by the time this call arrives. */
|
|
+static void
|
|
+connection_wait_for_bus (GDBusConnection *conn)
|
|
+{
|
|
+ GError *error = NULL;
|
|
+ GVariant *call_result;
|
|
+
|
|
+ call_result = g_dbus_connection_call_sync (conn,
|
|
+ DBUS_SERVICE_DBUS,
|
|
+ DBUS_PATH_DBUS,
|
|
+ DBUS_INTERFACE_DBUS,
|
|
+ "GetId",
|
|
+ NULL, /* arguments */
|
|
+ NULL, /* result type */
|
|
+ G_DBUS_CALL_FLAGS_NONE,
|
|
+ -1,
|
|
+ NULL,
|
|
+ &error);
|
|
+ g_assert_no_error (error);
|
|
+ g_assert_nonnull (call_result);
|
|
+ g_variant_unref (call_result);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Called when the subscriber receives a message from any connection
|
|
+ * announcing that it has emitted all the signals that it plans to emit.
|
|
+ */
|
|
+static void
|
|
+subscriber_finished_cb (GDBusConnection *conn,
|
|
+ const char *sender_name,
|
|
+ const char *path,
|
|
+ const char *iface,
|
|
+ const char *member,
|
|
+ GVariant *parameters,
|
|
+ void *user_data)
|
|
+{
|
|
+ Fixture *f = user_data;
|
|
+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
|
|
+ guint i;
|
|
+
|
|
+ g_assert_true (conn == subscriber);
|
|
+
|
|
+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
|
|
+ {
|
|
+ if (g_str_equal (sender_name, f->unique_names[i]))
|
|
+ {
|
|
+ g_assert_false (f->finished[i]);
|
|
+ f->finished[i] = TRUE;
|
|
+
|
|
+ g_test_message ("Received Finished signal from %s %s",
|
|
+ test_conn_descriptions[i], sender_name);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_error ("Received Finished signal from unknown sender %s", sender_name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Called when we receive a signal, either via the GDBusProxy (proxy != NULL)
|
|
+ * or via the GDBusConnection (proxy == NULL).
|
|
+ */
|
|
+static void
|
|
+fixture_received_signal (Fixture *f,
|
|
+ GDBusProxy *proxy,
|
|
+ const char *sender_name,
|
|
+ const char *path,
|
|
+ const char *iface,
|
|
+ const char *member,
|
|
+ GVariant *parameters)
|
|
+{
|
|
+ guint i;
|
|
+ ReceivedMessage *received;
|
|
+
|
|
+ /* Ignore the Finished signal if it matches a wildcard subscription */
|
|
+ if (g_str_equal (member, FINISHED_SIGNAL))
|
|
+ return;
|
|
+
|
|
+ received = g_new0 (ReceivedMessage, 1);
|
|
+
|
|
+ if (proxy != NULL)
|
|
+ received->received_by_proxy = g_object_ref (proxy);
|
|
+ else
|
|
+ received->received_by_proxy = NULL;
|
|
+
|
|
+ received->path = g_strdup (path);
|
|
+ received->iface = g_strdup (iface);
|
|
+ received->member = g_strdup (member);
|
|
+ received->parameters = g_variant_ref (parameters);
|
|
+
|
|
+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
|
|
+ {
|
|
+ if (g_str_equal (sender_name, f->unique_names[i]))
|
|
+ {
|
|
+ received->sender = i;
|
|
+ g_assert_false (f->finished[i]);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ g_assert_cmpint (received->sender, !=, TEST_CONN_NONE);
|
|
+
|
|
+ g_test_message ("Signal received from %s %s via %s",
|
|
+ test_conn_descriptions[received->sender],
|
|
+ sender_name,
|
|
+ proxy != NULL ? "proxy" : "connection");
|
|
+ g_test_message ("\tPath: %s", path);
|
|
+ g_test_message ("\tInterface: %s", iface);
|
|
+ g_test_message ("\tMember: %s", member);
|
|
+
|
|
+ if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(su)")))
|
|
+ {
|
|
+ g_variant_get (parameters, "(su)", &received->arg0, &received->step);
|
|
+ g_test_message ("\tString argument 0: %s", received->arg0);
|
|
+ g_test_message ("\tSent in step: %u", received->step);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(uu)");
|
|
+ g_variant_get (parameters, "(uu)", NULL, &received->step);
|
|
+ g_test_message ("\tArgument 0: (not a string)");
|
|
+ g_test_message ("\tSent in step: %u", received->step);
|
|
+ }
|
|
+
|
|
+ g_ptr_array_add (f->received, g_steal_pointer (&received));
|
|
+}
|
|
+
|
|
+static void
|
|
+proxy_signal_cb (GDBusProxy *proxy,
|
|
+ const char *sender_name,
|
|
+ const char *member,
|
|
+ GVariant *parameters,
|
|
+ void *user_data)
|
|
+{
|
|
+ Fixture *f = user_data;
|
|
+
|
|
+ fixture_received_signal (f, proxy, sender_name,
|
|
+ g_dbus_proxy_get_object_path (proxy),
|
|
+ g_dbus_proxy_get_interface_name (proxy),
|
|
+ member, parameters);
|
|
+}
|
|
+
|
|
+static void
|
|
+subscribed_signal_cb (GDBusConnection *conn,
|
|
+ const char *sender_name,
|
|
+ const char *path,
|
|
+ const char *iface,
|
|
+ const char *member,
|
|
+ GVariant *parameters,
|
|
+ void *user_data)
|
|
+{
|
|
+ Fixture *f = user_data;
|
|
+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
|
|
+
|
|
+ g_assert_true (conn == subscriber);
|
|
+
|
|
+ fixture_received_signal (f, NULL, sender_name, path, iface, member, parameters);
|
|
+}
|
|
+
|
|
+static void
|
|
+fixture_subscribe (Fixture *f,
|
|
+ const TestSubscribe *subscribe,
|
|
+ guint step_number)
|
|
+{
|
|
+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
|
|
+ const char *sender;
|
|
+
|
|
+ if (subscribe->sender != TEST_CONN_NONE)
|
|
+ {
|
|
+ sender = f->unique_names[subscribe->sender];
|
|
+ g_test_message ("\tSender: %s %s",
|
|
+ test_conn_descriptions[subscribe->sender],
|
|
+ sender);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sender = NULL;
|
|
+ g_test_message ("\tSender: (any)");
|
|
+ }
|
|
+
|
|
+ g_test_message ("\tPath: %s", nonnull (subscribe->path, "(any)"));
|
|
+ g_test_message ("\tInterface: %s",
|
|
+ nonnull (subscribe->iface, "(any)"));
|
|
+ g_test_message ("\tMember: %s",
|
|
+ nonnull (subscribe->member, "(any)"));
|
|
+ g_test_message ("\tString argument 0: %s",
|
|
+ nonnull (subscribe->arg0, "(any)"));
|
|
+ g_test_message ("\tFlags: %x", subscribe->flags);
|
|
+
|
|
+ if (f->mode != SUBSCRIPTION_MODE_PROXY)
|
|
+ {
|
|
+ /* CONN or PARALLEL */
|
|
+ guint id;
|
|
+
|
|
+ g_test_message ("\tSubscribing via connection");
|
|
+ id = g_dbus_connection_signal_subscribe (subscriber,
|
|
+ sender,
|
|
+ subscribe->iface,
|
|
+ subscribe->member,
|
|
+ subscribe->path,
|
|
+ subscribe->arg0,
|
|
+ subscribe->flags,
|
|
+ subscribed_signal_cb,
|
|
+ f, NULL);
|
|
+ g_assert_cmpuint (id, !=, 0);
|
|
+ f->subscriptions[step_number] = id;
|
|
+ }
|
|
+
|
|
+ if (f->mode != SUBSCRIPTION_MODE_CONN)
|
|
+ {
|
|
+ /* PROXY or PARALLEL */
|
|
+
|
|
+ if (sender == NULL)
|
|
+ {
|
|
+ g_test_message ("\tCannot subscribe via proxy: no bus name");
|
|
+ }
|
|
+ else if (subscribe->path == NULL)
|
|
+ {
|
|
+ g_test_message ("\tCannot subscribe via proxy: no path");
|
|
+ }
|
|
+ else if (subscribe->iface == NULL)
|
|
+ {
|
|
+ g_test_message ("\tCannot subscribe via proxy: no interface");
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ GDBusProxy *proxy;
|
|
+
|
|
+ g_test_message ("\tSubscribing via proxy");
|
|
+ proxy = g_dbus_proxy_new_sync (subscriber,
|
|
+ (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
|
|
+ | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
|
|
+ NULL, /* GDBusInterfaceInfo */
|
|
+ sender,
|
|
+ subscribe->path,
|
|
+ subscribe->iface,
|
|
+ NULL, /* GCancellable */
|
|
+ &f->error);
|
|
+ g_assert_no_error (f->error);
|
|
+ g_assert_nonnull (proxy);
|
|
+ g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal_cb), f);
|
|
+ g_ptr_array_add (f->proxies, g_steal_pointer (&proxy));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* As in setup(), we need to wait for AddMatch to happen. */
|
|
+ g_test_message ("Waiting for AddMatch to be processed");
|
|
+ connection_wait_for_bus (subscriber);
|
|
+}
|
|
+
|
|
+static void
|
|
+fixture_emit_signal (Fixture *f,
|
|
+ const TestEmitSignal *signal,
|
|
+ guint step_number)
|
|
+{
|
|
+ GVariant *body;
|
|
+ const char *destination;
|
|
+ gboolean ok;
|
|
+
|
|
+ g_test_message ("\tSender: %s",
|
|
+ test_conn_descriptions[signal->sender]);
|
|
+
|
|
+ if (signal->unicast_to != TEST_CONN_NONE)
|
|
+ {
|
|
+ destination = f->unique_names[signal->unicast_to];
|
|
+ g_test_message ("\tDestination: %s %s",
|
|
+ test_conn_descriptions[signal->unicast_to],
|
|
+ destination);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ destination = NULL;
|
|
+ g_test_message ("\tDestination: (broadcast)");
|
|
+ }
|
|
+
|
|
+ g_assert_nonnull (signal->path);
|
|
+ g_test_message ("\tPath: %s", signal->path);
|
|
+ g_assert_nonnull (signal->iface);
|
|
+ g_test_message ("\tInterface: %s", signal->iface);
|
|
+ g_assert_nonnull (signal->member);
|
|
+ g_test_message ("\tMember: %s", signal->member);
|
|
+
|
|
+ /* If arg0 is non-NULL, put it in the message's argument 0.
|
|
+ * Otherwise put something that will not match any arg0.
|
|
+ * Either way, put the sequence number in argument 1 so we can
|
|
+ * correlate sent messages with received messages later. */
|
|
+ if (signal->arg0 != NULL)
|
|
+ {
|
|
+ g_test_message ("\tString argument 0: %s", signal->arg0);
|
|
+ /* floating */
|
|
+ body = g_variant_new ("(su)", signal->arg0, (guint32) step_number);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_test_message ("\tArgument 0: (not a string)");
|
|
+ body = g_variant_new ("(uu)", (guint32) 0, (guint32) step_number);
|
|
+ }
|
|
+
|
|
+ ok = g_dbus_connection_emit_signal (f->conns[signal->sender],
|
|
+ destination,
|
|
+ signal->path,
|
|
+ signal->iface,
|
|
+ signal->member,
|
|
+ /* steals floating reference */
|
|
+ g_steal_pointer (&body),
|
|
+ &f->error);
|
|
+ g_assert_no_error (f->error);
|
|
+ g_assert_true (ok);
|
|
+
|
|
+ /* Emitting the signal is asynchronous, so if we want subsequent steps
|
|
+ * to be guaranteed to happen after the signal from the message bus's
|
|
+ * perspective, we have to do a round-trip to the message bus to sync up. */
|
|
+ g_test_message ("Waiting for signal to reach message bus");
|
|
+ connection_wait_for_bus (f->conns[signal->sender]);
|
|
+}
|
|
+
|
|
+static void
|
|
+fixture_run_plan (Fixture *f,
|
|
+ const TestPlan *plan,
|
|
+ SubscriptionMode mode)
|
|
+{
|
|
+ guint i;
|
|
+
|
|
+ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->subscriptions));
|
|
+ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_conn));
|
|
+ G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_proxy));
|
|
+
|
|
+ f->mode = mode;
|
|
+ f->plan = plan;
|
|
+
|
|
+ g_test_summary (plan->description);
|
|
+
|
|
+ for (i = 0; i < G_N_ELEMENTS (plan->steps); i++)
|
|
+ {
|
|
+ const TestStep *step = &plan->steps[i];
|
|
+
|
|
+ switch (step->action)
|
|
+ {
|
|
+ case TEST_ACTION_SUBSCRIBE:
|
|
+ g_test_message ("Step %u: adding subscription", i);
|
|
+ fixture_subscribe (f, &step->u.subscribe, i);
|
|
+ break;
|
|
+
|
|
+ case TEST_ACTION_EMIT_SIGNAL:
|
|
+ g_test_message ("Step %u: emitting signal", i);
|
|
+ fixture_emit_signal (f, &step->u.signal, i);
|
|
+ break;
|
|
+
|
|
+ case TEST_ACTION_NONE:
|
|
+ /* Padding to fill the rest of the array, do nothing */
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ g_return_if_reached ();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Now that we have done everything we wanted to do, emit Finished
|
|
+ * from each connection. */
|
|
+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
|
|
+ {
|
|
+ gboolean ok;
|
|
+
|
|
+ ok = g_dbus_connection_emit_signal (f->conns[i],
|
|
+ NULL,
|
|
+ FINISHED_PATH,
|
|
+ FINISHED_INTERFACE,
|
|
+ FINISHED_SIGNAL,
|
|
+ NULL,
|
|
+ &f->error);
|
|
+ g_assert_no_error (f->error);
|
|
+ g_assert_true (ok);
|
|
+ }
|
|
+
|
|
+ /* Wait until we have seen the Finished signal from each sender */
|
|
+ while (TRUE)
|
|
+ {
|
|
+ gboolean all_finished = TRUE;
|
|
+
|
|
+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
|
|
+ all_finished = all_finished && f->finished[i];
|
|
+
|
|
+ if (all_finished)
|
|
+ break;
|
|
+
|
|
+ g_main_context_iteration (NULL, TRUE);
|
|
+ }
|
|
+
|
|
+ /* Assert that the correct things happened before each Finished signal */
|
|
+ for (i = 0; i < f->received->len; i++)
|
|
+ {
|
|
+ const ReceivedMessage *received = g_ptr_array_index (f->received, i);
|
|
+
|
|
+ g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn));
|
|
+ g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy));
|
|
+ g_assert_cmpint (plan->steps[received->step].action,
|
|
+ ==, TEST_ACTION_EMIT_SIGNAL);
|
|
+
|
|
+ if (received->received_by_proxy != NULL)
|
|
+ f->received_by_proxy[received->step] += 1;
|
|
+ else
|
|
+ f->received_by_conn[received->step] += 1;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < G_N_ELEMENTS (plan->steps); i++)
|
|
+ {
|
|
+ const TestStep *step = &plan->steps[i];
|
|
+
|
|
+ if (step->action == TEST_ACTION_EMIT_SIGNAL)
|
|
+ {
|
|
+ const TestEmitSignal *signal = &plan->steps[i].u.signal;
|
|
+
|
|
+ if (mode != SUBSCRIPTION_MODE_PROXY)
|
|
+ {
|
|
+ g_test_message ("Signal from step %u was received %u times by "
|
|
+ "GDBusConnection, expected %u",
|
|
+ i, f->received_by_conn[i], signal->received_by_conn);
|
|
+ g_assert_cmpuint (f->received_by_conn[i], ==, signal->received_by_conn);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_assert_cmpuint (f->received_by_conn[i], ==, 0);
|
|
+ }
|
|
+
|
|
+ if (mode != SUBSCRIPTION_MODE_CONN)
|
|
+ {
|
|
+ g_test_message ("Signal from step %u was received %u times by "
|
|
+ "GDBusProxy, expected %u",
|
|
+ i, f->received_by_proxy[i], signal->received_by_proxy);
|
|
+ g_assert_cmpuint (f->received_by_proxy[i], ==, signal->received_by_proxy);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ g_assert_cmpuint (f->received_by_proxy[i], ==, 0);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+setup (Fixture *f,
|
|
+ G_GNUC_UNUSED const void *context)
|
|
+{
|
|
+ GDBusConnection *subscriber;
|
|
+ guint i;
|
|
+
|
|
+ session_bus_up ();
|
|
+
|
|
+ f->proxies = g_ptr_array_new_full (MAX_TEST_STEPS, g_object_unref);
|
|
+ f->received = g_ptr_array_new_full (MAX_TEST_STEPS,
|
|
+ (GDestroyNotify) received_message_free);
|
|
+
|
|
+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
|
|
+ {
|
|
+ f->conns[i] = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &f->error);
|
|
+ g_assert_no_error (f->error);
|
|
+ g_assert_nonnull (f->conns[i]);
|
|
+
|
|
+ f->unique_names[i] = g_dbus_connection_get_unique_name (f->conns[i]);
|
|
+ g_assert_nonnull (f->unique_names[i]);
|
|
+ g_test_message ("%s is %s",
|
|
+ test_conn_descriptions[i],
|
|
+ f->unique_names[i]);
|
|
+ }
|
|
+
|
|
+ subscriber = f->conns[TEST_CONN_SUBSCRIBER];
|
|
+
|
|
+ /* Used to wait for all connections to finish sending whatever they
|
|
+ * wanted to send */
|
|
+ f->finished_subscription = g_dbus_connection_signal_subscribe (subscriber,
|
|
+ NULL,
|
|
+ FINISHED_INTERFACE,
|
|
+ FINISHED_SIGNAL,
|
|
+ FINISHED_PATH,
|
|
+ NULL,
|
|
+ G_DBUS_SIGNAL_FLAGS_NONE,
|
|
+ subscriber_finished_cb,
|
|
+ f, NULL);
|
|
+ /* AddMatch is sent asynchronously, so we don't know how
|
|
+ * soon it will be processed. Before emitting signals, we
|
|
+ * need to wait for the message bus to get as far as processing
|
|
+ * AddMatch. */
|
|
+ g_test_message ("Waiting for AddMatch to be processed");
|
|
+ connection_wait_for_bus (subscriber);
|
|
+}
|
|
+
|
|
+static void
|
|
+test_conn_subscribe (Fixture *f,
|
|
+ const void *context)
|
|
+{
|
|
+ fixture_run_plan (f, context, SUBSCRIPTION_MODE_CONN);
|
|
+}
|
|
+
|
|
+static void
|
|
+test_proxy_subscribe (Fixture *f,
|
|
+ const void *context)
|
|
+{
|
|
+ fixture_run_plan (f, context, SUBSCRIPTION_MODE_PROXY);
|
|
+}
|
|
+
|
|
+static void
|
|
+test_parallel_subscribe (Fixture *f,
|
|
+ const void *context)
|
|
+{
|
|
+ fixture_run_plan (f, context, SUBSCRIPTION_MODE_PARALLEL);
|
|
+}
|
|
+
|
|
+static void
|
|
+teardown (Fixture *f,
|
|
+ G_GNUC_UNUSED const void *context)
|
|
+{
|
|
+ GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
|
|
+ guint i;
|
|
+
|
|
+ g_ptr_array_unref (f->proxies);
|
|
+
|
|
+ if (f->finished_subscription != 0)
|
|
+ g_dbus_connection_signal_unsubscribe (subscriber, f->finished_subscription);
|
|
+
|
|
+ for (i = 0; i < G_N_ELEMENTS (f->subscriptions); i++)
|
|
+ {
|
|
+ if (f->subscriptions[i] != 0)
|
|
+ g_dbus_connection_signal_unsubscribe (subscriber, f->subscriptions[i]);
|
|
+ }
|
|
+
|
|
+ g_ptr_array_unref (f->received);
|
|
+
|
|
+ for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
|
|
+ g_clear_object (&f->conns[i]);
|
|
+
|
|
+ g_clear_error (&f->error);
|
|
+
|
|
+ session_bus_down ();
|
|
+}
|
|
+
|
|
+int
|
|
+main (int argc,
|
|
+ char *argv[])
|
|
+{
|
|
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
|
|
+
|
|
+ g_test_dbus_unset ();
|
|
+
|
|
+#define ADD_SUBSCRIBE_TEST(name) \
|
|
+ do { \
|
|
+ g_test_add ("/gdbus/subscribe/conn/" #name, \
|
|
+ Fixture, &plan_ ## name, \
|
|
+ setup, test_conn_subscribe, teardown); \
|
|
+ g_test_add ("/gdbus/subscribe/proxy/" #name, \
|
|
+ Fixture, &plan_ ## name, \
|
|
+ setup, test_proxy_subscribe, teardown); \
|
|
+ g_test_add ("/gdbus/subscribe/parallel/" #name, \
|
|
+ Fixture, &plan_ ## name, \
|
|
+ setup, test_parallel_subscribe, teardown); \
|
|
+ } while (0)
|
|
+
|
|
+ ADD_SUBSCRIBE_TEST (simple);
|
|
+ ADD_SUBSCRIBE_TEST (broadcast_from_anyone);
|
|
+ ADD_SUBSCRIBE_TEST (match_twice);
|
|
+ ADD_SUBSCRIBE_TEST (limit_by_unique_name);
|
|
+
|
|
+ return g_test_run();
|
|
+}
|
|
--- a/gio/tests/meson.build
|
|
+++ b/gio/tests/meson.build
|
|
@@ -374,6 +374,7 @@ if host_machine.system() != 'windows'
|
|
},
|
|
'gdbus-proxy-unique-name' : {'extra_sources' : extra_sources},
|
|
'gdbus-proxy-well-known-name' : {'extra_sources' : extra_sources},
|
|
+ 'gdbus-subscribe' : {'extra_sources' : extra_sources},
|
|
'gdbus-test-codegen' : {
|
|
'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info],
|
|
'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'],
|