From 47be0f7a233e673a74566e9fb668955cc76fbdab Mon Sep 17 00:00:00 2001
From: Cosimo Cecchi <cosimoc@gnome.org>
Date: Wed, 6 Dec 2017 10:34:17 -0800
Subject: [PATCH 244/682] gdbusproxy: make g-name-owner property useful with
 unique names

Currently, GDBusProxy:g-name-owner only notifies changes to the unique
name owner of the remote object in case the proxy was constructed for a
well-known name.
That sounds like an artificial restriction, and it's convenient to
connect to notify::g-name-owner if a proxy instance has already been
created for an unique name, instead of additionally using
g_bus_watch_name() to track the owner.

To fix this, always connect to NameOwnerChanged after the proxy is
initialized, instead of only doing so when the proxy was constructed for
a well-known name.

https://bugzilla.gnome.org/show_bug.cgi?id=791316
https://gitlab.gnome.org/GNOME/glib/issues/1310
---
 gio/gdbusproxy.c                    |   5 +-
 gio/tests/Makefile.am               |   2 +
 gio/tests/gdbus-proxy-unique-name.c | 215 ++++++++++++++++++++++++++++
 gio/tests/meson.build               |   1 +
 4 files changed, 220 insertions(+), 3 deletions(-)
 create mode 100644 gio/tests/gdbus-proxy-unique-name.c

diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c
index 6d84deced..9cfb82d70 100644
--- a/gio/gdbusproxy.c
+++ b/gio/gdbusproxy.c
@@ -59,8 +59,7 @@
  * well-known name, the property cache is flushed when the name owner
  * vanishes and reloaded when a name owner appears.
  *
- * If a #GDBusProxy is used for a well-known name, the owner of the
- * name is tracked and can be read from
+ * The unique name owner of the proxy's name is tracked and can be read from
  * #GDBusProxy:g-name-owner. Connect to the #GObject::notify signal to
  * get notified of changes. Additionally, only signals and property
  * changes emitted from the current name owner are considered and
@@ -1769,7 +1768,7 @@ async_initable_init_first (GAsyncInitable *initable)
                                             (GDestroyNotify) signal_subscription_unref);
     }
 
-  if (proxy->priv->name != NULL && !g_dbus_is_unique_name (proxy->priv->name))
+  if (proxy->priv->name != NULL)
     {
       proxy->priv->name_owner_changed_subscription_id =
         g_dbus_connection_signal_subscribe (proxy->priv->connection,
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index b8a414954..8efb1eaa0 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -449,6 +449,7 @@ test_programs += \
 	gdbus-peer				\
 	gdbus-proxy				\
 	gdbus-proxy-threads			\
+	gdbus-proxy-unique-name			\
 	gdbus-proxy-well-known-name		\
 	gdbus-test-codegen			\
 	gdbus-test-codegen-old			\
@@ -483,6 +484,7 @@ gdbus_introspection_SOURCES              = $(gdbus_sessionbus_sources) gdbus-int
 gdbus_names_SOURCES                      = $(gdbus_sessionbus_sources) gdbus-names.c
 gdbus_proxy_SOURCES                      = $(gdbus_sessionbus_sources) gdbus-proxy.c
 gdbus_proxy_threads_SOURCES              = $(gdbus_sessionbus_sources) gdbus-proxy-threads.c
+gdbus_proxy_unique_name_SOURCES          = $(gdbus_sessionbus_sources) gdbus-proxy-unique-name.c
 gdbus_proxy_well_known_name_SOURCES      = $(gdbus_sessionbus_sources) gdbus-proxy-well-known-name.c
 gdbus_test_codegen_SOURCES               = $(gdbus_sessionbus_sources) gdbus-test-codegen.c
 nodist_gdbus_test_codegen_SOURCES        = gdbus-test-codegen-generated.c gdbus-test-codegen-generated.h
diff --git a/gio/tests/gdbus-proxy-unique-name.c b/gio/tests/gdbus-proxy-unique-name.c
new file mode 100644
index 000000000..eacb0219c
--- /dev/null
+++ b/gio/tests/gdbus-proxy-unique-name.c
@@ -0,0 +1,215 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright (C) 2008-2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Cosimo Cecchi <cosimoc@gnome.org>
+ */
+
+#include <gio/gio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "gdbus-tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+proxy_new_cb (GObject       *source_object,
+              GAsyncResult  *res,
+              gpointer       user_data)
+{
+  GDBusProxy **ret = user_data;
+  GError *error;
+
+  error = NULL;
+  *ret = g_dbus_proxy_new_finish (res, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (ret);
+
+  g_main_loop_quit (loop);
+}
+
+static void
+test_proxy_unique_name (void)
+{
+  GDBusProxy *wp;
+  GDBusProxy *p;
+  GDBusProxy *ap;
+  GDBusConnection *c;
+  GError *error;
+  gchar *name_owner;
+  gchar **property_names;
+  GVariant *variant;
+  GVariant *result;
+  char *unique_name;
+
+  session_bus_up ();
+
+  error = NULL;
+  c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (c);
+
+  /* use a proxy to the well-known name to set things up */
+  wp = g_dbus_proxy_new_sync (c,
+                              G_DBUS_PROXY_FLAGS_NONE,
+                              NULL,                      /* GDBusInterfaceInfo* */
+                              "com.example.TestService", /* name */
+                              "/com/example/TestObject", /* object path */
+                              "com.example.Frob",        /* interface name */
+                              NULL,                      /* GCancellable */
+                              &error);
+  g_assert_no_error (error);
+
+  /* this is safe; testserver will exit once the bus goes away */
+  g_assert_true (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL));
+
+  /* check that we get the notify::g-name-owner signal */
+  _g_assert_property_notify (wp, "g-name-owner");
+
+  /* now get the unique name of testserver's connection */
+  unique_name = g_dbus_proxy_get_name_owner (wp);
+
+  /* if we create another a proxy with the service being available, check that
+   * it has a name owner and properties
+   */
+  error = NULL;
+  p = g_dbus_proxy_new_sync (c,
+                              G_DBUS_PROXY_FLAGS_NONE,
+                              NULL,                      /* GDBusInterfaceInfo* */
+                              unique_name,               /* name */
+                              "/com/example/TestObject", /* object path */
+                              "com.example.Frob",        /* interface name */
+                              NULL,                      /* GCancellable */
+                              &error);
+  g_assert_no_error (error);
+  name_owner = g_dbus_proxy_get_name_owner (p);
+  property_names = g_dbus_proxy_get_cached_property_names (p);
+  g_assert_true (g_dbus_is_unique_name (name_owner));
+  g_assert_nonnull (property_names);
+  g_assert_cmpint (g_strv_length (property_names), >, 0);
+  g_free (name_owner);
+  g_strfreev (property_names);
+
+  /* also for async: we should have a name owner and cached properties */
+  g_dbus_proxy_new (c,
+                    G_DBUS_PROXY_FLAGS_NONE,
+                    NULL,                      /* GDBusInterfaceInfo* */
+                    unique_name,               /* name */
+                    "/com/example/TestObject", /* object path */
+                    "com.example.Frob",        /* interface name */
+                    NULL,                      /* GCancellable */
+                    (GAsyncReadyCallback) proxy_new_cb,
+                    &ap);
+  g_main_loop_run (loop);
+  name_owner = g_dbus_proxy_get_name_owner (ap);
+  property_names = g_dbus_proxy_get_cached_property_names (ap);
+  g_assert_true (g_dbus_is_unique_name (name_owner));
+  g_assert_nonnull (property_names);
+  g_assert_cmpint (g_strv_length (property_names), >, 0);
+  g_free (name_owner);
+  g_strfreev (property_names);
+
+  /* Check property value is the initial value */
+  variant = g_dbus_proxy_get_cached_property (p, "y");
+  g_assert_nonnull (variant);
+  g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
+  g_variant_unref (variant);
+  variant = g_dbus_proxy_get_cached_property (ap, "y");
+  g_assert_nonnull (variant);
+  g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
+  g_variant_unref (variant);
+
+  /* Check that properties are updated on p */
+  result = g_dbus_proxy_call_sync (p,
+                                   "FrobSetProperty",
+                                   g_variant_new ("(sv)",
+                                                  "y",
+                                                  g_variant_new_byte (42)),
+                                   G_DBUS_CALL_FLAGS_NONE,
+                                   -1,
+                                   NULL,
+                                   &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (result);
+  g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
+  g_variant_unref (result);
+  _g_assert_signal_received (p, "g-properties-changed");
+  variant = g_dbus_proxy_get_cached_property (p, "y");
+  g_assert_nonnull (variant);
+  g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
+  g_variant_unref (variant);
+  variant = g_dbus_proxy_get_cached_property (ap, "y");
+  g_assert_nonnull (variant);
+  g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
+  g_variant_unref (variant);
+
+  /* Nuke the service and check that we get the signal and then don't
+   * have a name owner nor any cached properties
+   */
+  result = g_dbus_proxy_call_sync (p,
+                                   "Quit",
+                                   NULL,
+                                   G_DBUS_CALL_FLAGS_NONE,
+                                   -1,
+                                   NULL,
+                                   &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (result);
+  g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
+  g_variant_unref (result);
+  /* and wait... */
+  _g_assert_property_notify (p, "g-name-owner");
+  /* now we shouldn't have a name owner nor any cached properties */
+  g_assert_cmpstr (g_dbus_proxy_get_name_owner (p), ==, NULL);
+  g_assert_null (g_dbus_proxy_get_cached_property_names (p));
+  g_assert_null (g_dbus_proxy_get_cached_property (p, "y"));
+
+  g_object_unref (p);
+  g_object_unref (ap);
+
+  g_object_unref (wp);
+  g_free (unique_name);
+
+  g_object_unref (c);
+
+  /* tear down bus */
+  session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int   argc,
+      char *argv[])
+{
+  gint ret;
+
+  g_test_init (&argc, &argv, NULL);
+
+  /* all the tests rely on a shared main loop */
+  loop = g_main_loop_new (NULL, FALSE);
+
+  g_test_dbus_unset ();
+
+  g_test_add_func ("/gdbus/proxy-unique-name", test_proxy_unique_name);
+
+  ret = g_test_run();
+  return ret;
+}
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index b71f61704..61c6be096 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -221,6 +221,7 @@ if host_machine.system() != 'windows'
         'extra_sources' : extra_sources,
         'dependencies' : [dbus1_dep],
       },
+      'gdbus-proxy-unique-name' : {'extra_sources' : extra_sources},
       'gdbus-proxy-well-known-name' : {'extra_sources' : extra_sources},
       'gdbus-test-codegen' : {
         'extra_sources' : [extra_sources, gdbus_test_codegen_generated],
-- 
2.19.1

