[dconf/dbus1] Add support for libdbus-1
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dconf/dbus1] Add support for libdbus-1
- Date: Mon, 20 Dec 2010 15:16:00 +0000 (UTC)
commit 0bc13626b1dd2ebb1b9897fb188a119aee19597e
Author: Ryan Lortie <desrt desrt ca>
Date: Mon Dec 20 10:14:19 2010 -0500
Add support for libdbus-1
Makefile.am | 2 +-
configure.ac | 6 +-
dbus-1/.gitignore | 3 +
dbus-1/Makefile.am | 25 ++
dbus-1/dconf-dbus-1.c | 630 +++++++++++++++++++++++++++++++++++++++++++++++++
dbus-1/dconf-dbus-1.h | 45 ++++
tests/.gitignore | 1 +
tests/Makefile.am | 5 +-
tests/dbus1.c | 377 +++++++++++++++++++++++++++++
9 files changed, 1089 insertions(+), 5 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 219d5a1..148cb94 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
-SUBDIRS = gvdb service gsettings tests client bin engine common docs
+SUBDIRS = gvdb service gsettings tests client bin engine common docs dbus-1
if ENABLE_EDITOR
SUBDIRS += editor
diff --git a/configure.ac b/configure.ac
index 309e2da..61a045d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,8 +21,9 @@ GOBJECT_INTROSPECTION_CHECK([0.9.5])
GTK_DOC_CHECK([1.15])
# Dependencies
-PKG_CHECK_MODULES(gio, gio-2.0 >= 2.25.16)
-
+PKG_CHECK_MODULES(glib, glib-2.0 >= 2.25.16)
+PKG_CHECK_MODULES(gio, gio-2.0)
+PKG_CHECK_MODULES(dbus, dbus-1)
AC_ARG_ENABLE(editor,
AC_HELP_STRING([--disable-editor],
@@ -55,6 +56,7 @@ AC_CONFIG_FILES([
client/dconf.pc
client/Makefile
service/Makefile
+ dbus-1/Makefile
bin/Makefile
editor/Makefile
tests/Makefile
diff --git a/dbus-1/.gitignore b/dbus-1/.gitignore
new file mode 100644
index 0000000..3ea6490
--- /dev/null
+++ b/dbus-1/.gitignore
@@ -0,0 +1,3 @@
+libdconf-dbus-1.so
+libdconf-dbus-1.so.0
+libdconf-dbus-1.so.0.0.0
diff --git a/dbus-1/Makefile.am b/dbus-1/Makefile.am
new file mode 100644
index 0000000..c9bd7ba
--- /dev/null
+++ b/dbus-1/Makefile.am
@@ -0,0 +1,25 @@
+AM_CFLAGS = -std=c89 -Wall -Wmissing-prototypes -Wwrite-strings -fPIC -DPIC
+INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/gvdb -I$(top_srcdir)/engine $(dbus_CFLAGS) $(glib_CFLAGS)
+
+shlibdir = $(libdir)
+shlib_PROGRAMS = libdconf-dbus-1.so.0.0.0
+
+libdconf_dbus_1_so_0_0_0_LDADD = $(glib_LIBS) $(dbus_LIBS)
+libdconf_dbus_1_so_0_0_0_LDFLAGS = -module -avoid-version -shared
+libdconf_dbus_1_so_0_0_0_SOURCES = \
+ ../engine/dconf-engine.c \
+ ../common/dconf-shmdir.c \
+ ../gvdb/gvdb-reader.c \
+ dconf-dbus-1.c
+
+libdconf-dbus-1.so.0 libdconf-dbus-1.so: libdconf-dbus-1.so.0.0.0
+ $(AM_V_GEN) ln -fs libdconf-dbus-1.so.0.0.0 $@
+
+install-data-hook:
+ ln -fs libdconf-dbus-1.so.0.0.0 $(DESTDIR)$(shlibdir)/libdconf-dbus-1.so.0
+ ln -fs libdconf-dbus-1.so.0.0.0 $(DESTDIR)$(shlibdir)/libdconf-dbus-1.so
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(shlibdir)/libdconf-dbus-1.so.0
+ rm -f $(DESTDIR)$(shlibdir)/libdconf-dbus-1.so
+
diff --git a/dbus-1/dconf-dbus-1.c b/dbus-1/dconf-dbus-1.c
new file mode 100644
index 0000000..c3a75bc
--- /dev/null
+++ b/dbus-1/dconf-dbus-1.c
@@ -0,0 +1,630 @@
+/**
+ * Copyright © 2010 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the licence, or (at
+ * your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ **/
+
+#include "dconf-dbus-1.h"
+
+#include <dconf-engine.h>
+
+#include <string.h>
+
+typedef struct _Outstanding Outstanding;
+
+struct _DConfDBusClient
+{
+ DBusConnection *session_bus;
+ DBusConnection *system_bus;
+ guint session_filter;
+ guint system_filter;
+ gint ref_count;
+
+ Outstanding *outstanding;
+ gchar *anti_expose_tag;
+
+ DConfEngine *engine;
+};
+
+static void
+dconf_dbus_client_add_value_to_iter (DBusMessageIter *iter,
+ GVariant *value)
+{
+ GVariantClass class;
+
+ class = g_variant_classify (value);
+
+ switch (class)
+ {
+ case G_VARIANT_CLASS_BOOLEAN:
+ {
+ dbus_bool_t boolean;
+
+ boolean = g_variant_get_boolean (value);
+ dbus_message_iter_append_basic (iter, 'b', &boolean);
+ }
+ break;
+
+ case G_VARIANT_CLASS_BYTE:
+ case G_VARIANT_CLASS_INT16:
+ case G_VARIANT_CLASS_UINT16:
+ case G_VARIANT_CLASS_INT32:
+ case G_VARIANT_CLASS_UINT32:
+ case G_VARIANT_CLASS_INT64:
+ case G_VARIANT_CLASS_UINT64:
+ case G_VARIANT_CLASS_DOUBLE:
+ dbus_message_iter_append_basic (iter, class,
+ g_variant_get_data (value));
+ break;
+
+ case G_VARIANT_CLASS_STRING:
+ case G_VARIANT_CLASS_OBJECT_PATH:
+ case G_VARIANT_CLASS_SIGNATURE:
+ {
+ const gchar *str;
+
+ str = g_variant_get_string (value, NULL);
+ dbus_message_iter_append_basic (iter, class, &str);
+ }
+ break;
+
+ case G_VARIANT_CLASS_ARRAY:
+ {
+ const gchar *contained;
+ DBusMessageIter sub;
+ gint i, n;
+
+ contained = g_variant_get_type_string (value) + 1;
+ n = g_variant_n_children (value);
+ dbus_message_iter_open_container (iter, 'a', contained, &sub);
+ for (i = 0; i < n; i++)
+ {
+ GVariant *child;
+
+ child = g_variant_get_child_value (value, i);
+ dconf_dbus_client_add_value_to_iter (&sub, child);
+ g_variant_unref (child);
+ }
+
+ dbus_message_iter_close_container (iter, &sub);
+ }
+ break;
+
+ case G_VARIANT_CLASS_TUPLE:
+ {
+ DBusMessageIter sub;
+ gint i, n;
+
+ n = g_variant_n_children (value);
+ dbus_message_iter_open_container (iter, 'r', NULL, &sub);
+ for (i = 0; i < n; i++)
+ {
+ GVariant *child;
+
+ child = g_variant_get_child_value (value, i);
+ dconf_dbus_client_add_value_to_iter (&sub, child);
+ g_variant_unref (child);
+ }
+
+ dbus_message_iter_close_container (iter, &sub);
+ }
+ break;
+
+ case G_VARIANT_CLASS_DICT_ENTRY:
+ {
+ DBusMessageIter sub;
+ gint i;
+
+ dbus_message_iter_open_container (iter, 'e', NULL, &sub);
+ for (i = 0; i < 2; i++)
+ {
+ GVariant *child;
+
+ child = g_variant_get_child_value (value, i);
+ dconf_dbus_client_add_value_to_iter (&sub, child);
+ g_variant_unref (child);
+ }
+
+ dbus_message_iter_close_container (iter, &sub);
+ }
+ break;
+
+ case G_VARIANT_CLASS_VARIANT:
+ {
+ DBusMessageIter sub;
+ GVariant *child;
+
+ child = g_variant_get_variant (value);
+ dbus_message_iter_open_container (iter, 'v',
+ g_variant_get_type_string (child),
+ &sub);
+ dconf_dbus_client_add_value_to_iter (&sub, child);
+ dbus_message_iter_close_container (iter, &sub);
+ g_variant_unref (child);
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+#if 0
+static void
+dconf_settings_backend_signal (DBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ DConfDBusClient *dcdbc = user_data;
+ const gchar *anti_expose;
+ const gchar **rels;
+ const gchar *path;
+ gchar bus_type;
+
+ if (connection == dcdbc->session_bus)
+ {
+ anti_expose = dcdbc->anti_expose_tag;
+ bus_type = 'e';
+ }
+
+ else if (connection == dcdbc->system_bus)
+ {
+ anti_expose = NULL;
+ bus_type = 'y';
+ }
+
+ else
+ g_assert_not_reached ();
+
+ if (dconf_engine_decode_notify (dcdbc->engine, anti_expose,
+ &path, &rels, bus_type,
+ sender_name, interface_name,
+ signal_name, parameters))
+ {
+ /** XXX EMIT CHANGES XXX **/
+ g_free (rels);
+ }
+}
+#endif
+
+static void
+dconf_settings_backend_send (DConfDBusClient *dcdbc,
+ DConfEngineMessage *dcem,
+ DBusPendingCallNotifyFunction callback,
+ gpointer user_data)
+{
+ DBusConnection *connection;
+ gint i;
+
+ for (i = 0; i < dcem->n_messages; i++)
+ {
+ switch (dcem->bus_types[i])
+ {
+ case 'e':
+ connection = dcdbc->session_bus;
+ break;
+
+ case 'y':
+ connection = dcdbc->system_bus;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (connection == NULL && callback != NULL)
+ callback (NULL, user_data);
+
+ if (connection != NULL)
+ {
+ DBusPendingCall *pending;
+ DBusMessageIter diter;
+ DBusMessage *message;
+ GVariantIter giter;
+ GVariant *child;
+
+ message = dbus_message_new_method_call (dcem->bus_name,
+ dcem->object_path,
+ dcem->interface_name,
+ dcem->method_name);
+
+ dbus_message_iter_init_append (message, &diter);
+ g_variant_iter_init (&giter, dcem->parameters[i]);
+
+ while ((child = g_variant_iter_next_value (&giter)))
+ {
+ dconf_dbus_client_add_value_to_iter (&diter, child);
+ g_variant_unref (child);
+ }
+
+ dbus_connection_send_with_reply (connection, message,
+ &pending, 120000);
+ dbus_pending_call_set_notify (pending, callback, user_data, NULL);
+ dbus_message_unref (message);
+ }
+ }
+}
+
+static GVariant *
+dconf_settings_backend_send_finish (DBusPendingCall *pending)
+{
+ DBusMessage *message;
+
+ if (pending == NULL)
+ return NULL;
+
+ message = dbus_pending_call_steal_reply (pending);
+ dbus_pending_call_unref (pending);
+
+ /* XXX remove args */
+
+ dbus_message_unref (message);
+
+ return g_variant_new ("()");
+}
+
+struct _Outstanding
+{
+ Outstanding *next;
+
+ DConfDBusClient *dcdbc;
+ DConfEngineMessage dcem;
+
+ gchar *set_key;
+ GVariant *set_value;
+ GTree *tree;
+};
+
+static void
+dconf_settings_backend_outstanding_returned (DBusPendingCall *pending,
+ gpointer user_data)
+{
+ Outstanding *outstanding = user_data;
+ DConfDBusClient *dcdbc;
+ GVariant *reply;
+
+ dcdbc = outstanding->dcdbc;
+
+ /* One way or another we no longer need this hooked into the list.
+ */
+ {
+ Outstanding **tmp;
+
+ for (tmp = &dcdbc->outstanding; tmp; tmp = &(*tmp)->next)
+ if (*tmp == outstanding)
+ {
+ *tmp = outstanding->next;
+ break;
+ }
+ }
+
+ reply = dconf_settings_backend_send_finish (pending);
+
+ if (reply)
+ {
+ /* Success.
+ *
+ * We want to ensure that we don't emit an extra change
+ * notification signal when we see the signal that the service is
+ * about to send, so store the tag so we know to ignore it when
+ * the signal comes.
+ *
+ * No thread safety issue here since this variable is only
+ * accessed from the worker thread.
+ */
+ g_free (dcdbc->anti_expose_tag);
+ g_variant_get_child (reply, 0, "s", &dcdbc->anti_expose_tag);
+ g_variant_unref (reply);
+ }
+ else
+ {
+ /* An error of some kind.
+ *
+ * We already removed the outstanding entry from the list, so the
+ * unmodified database is now visible to the client. Change
+ * notify so that they see it.
+ */
+ if (outstanding->set_key)
+ /* XXX emit */;
+ else
+ /* XXX emit */;
+ }
+
+ dconf_engine_message_destroy (&outstanding->dcem);
+ dconf_dbus_client_unref (outstanding->dcdbc);
+ g_free (outstanding->set_key);
+
+ if (outstanding->set_value)
+ g_variant_unref (outstanding->set_value);
+
+ if (outstanding->tree)
+ g_tree_unref (outstanding->tree);
+
+ g_slice_free (Outstanding, outstanding);
+}
+
+static void
+dconf_settings_backend_queue (DConfDBusClient *dcdbc,
+ DConfEngineMessage *dcem,
+ const gchar *set_key,
+ GVariant *set_value,
+ GTree *tree)
+{
+ Outstanding *outstanding;
+
+ outstanding = g_slice_new (Outstanding);
+ outstanding->dcdbc = dconf_dbus_client_ref (dcdbc);
+ outstanding->dcem = *dcem;
+
+ outstanding->set_key = g_strdup (set_key);
+ outstanding->set_value = set_value ? g_variant_ref_sink (set_value) : NULL;
+ outstanding->tree = tree ? g_tree_ref (tree) : NULL;
+
+ outstanding->next = dcdbc->outstanding;
+ dcdbc->outstanding = outstanding;
+
+ dconf_settings_backend_send (outstanding->dcdbc,
+ &outstanding->dcem,
+ dconf_settings_backend_outstanding_returned,
+ outstanding);
+}
+
+static gboolean
+dconf_settings_backend_scan_outstanding_tree (GTree *tree,
+ const gchar *key,
+ gsize key_length,
+ gpointer *value)
+{
+ gchar *mykey;
+
+ mykey = g_alloca (key_length + 1);
+ memcpy (mykey, key, key_length + 1);
+
+ while (!g_tree_lookup_extended (tree, mykey, NULL, value) &&
+ --key_length)
+ {
+ while (mykey[key_length - 1] != '/')
+ key_length--;
+
+ mykey[key_length] = '\0';
+ }
+
+ return key_length != 0;
+}
+
+static gboolean
+dconf_settings_backend_scan_outstanding (DConfDBusClient *backend,
+ const gchar *key,
+ GVariant **value)
+{
+ gboolean found = FALSE;
+ Outstanding *node;
+ gsize length;
+
+ length = strlen (key);
+
+ if G_LIKELY (backend->outstanding == NULL)
+ return FALSE;
+
+ for (node = backend->outstanding; node; node = node->next)
+ {
+ if (node->set_key)
+ {
+ if (strcmp (key, node->set_key) == 0)
+ {
+ if (node->set_value != NULL)
+ *value = g_variant_ref (node->set_value);
+ else
+ *value = NULL;
+
+ found = TRUE;
+ break;
+ }
+ }
+
+ else
+ {
+ gpointer result;
+
+ if (dconf_settings_backend_scan_outstanding_tree (node->tree, key,
+ length, &result))
+ {
+ if (result)
+ *value = g_variant_ref (result);
+ else
+ *value = NULL;
+
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+
+GVariant *
+dconf_dbus_client_read (DConfDBusClient *dcdbc,
+ const gchar *key)
+{
+ GVariant *value;
+
+ if (dconf_settings_backend_scan_outstanding (dcdbc, key, &value))
+ return value;
+
+ return dconf_engine_read (dcdbc->engine, key);
+}
+
+gboolean
+dconf_dbus_client_write (DConfDBusClient *dcdbc,
+ const gchar *key,
+ GVariant *value)
+{
+ DConfEngineMessage dcem;
+
+ if (!dconf_engine_write (dcdbc->engine, key, value, &dcem, NULL))
+ return FALSE;
+
+ dconf_settings_backend_queue (dcdbc, &dcem, key, value, NULL);
+ /* XXX emit */
+
+ return TRUE;
+}
+
+typedef struct
+{
+ DConfDBusClient *dcdbc;
+ guint64 state;
+ gchar name[1];
+} OutstandingWatch;
+
+static OutstandingWatch *
+outstanding_watch_new (DConfDBusClient *dcdbc,
+ const gchar *name)
+{
+ OutstandingWatch *watch;
+ gsize length;
+
+ length = strlen (name);
+ watch = g_malloc (G_STRUCT_OFFSET (OutstandingWatch, name) + length + 1);
+ watch->dcdbc = dconf_dbus_client_ref (dcdbc);
+ watch->state = dconf_engine_get_state (dcdbc->engine);
+ strcpy (watch->name, name);
+
+ return watch;
+}
+
+static void
+outstanding_watch_free (OutstandingWatch *watch)
+{
+ dconf_dbus_client_unref (watch->dcdbc);
+ g_free (watch);
+}
+
+static void
+add_match_done (DBusPendingCall *pending,
+ gpointer user_data)
+{
+ OutstandingWatch *watch = user_data;
+ GError *error = NULL;
+ GVariant *reply;
+
+ reply = dconf_settings_backend_send_finish (pending);
+
+ if (reply == NULL)
+ {
+ g_critical ("DBus AddMatch for dconf path '%s': %s",
+ watch->name, error->message);
+ outstanding_watch_free (watch);
+ g_error_free (error);
+
+ return;
+ }
+
+ else
+ g_variant_unref (reply); /* it is just an empty tuple */
+
+ /* In the normal case we can just free everything and be done.
+ *
+ * There is a fleeting chance, however, that the database has changed
+ * in the meantime. In that case we can only assume that the subject
+ * of this watch changed in that time period and emit a signal to that
+ * effect.
+ */
+ if (dconf_engine_get_state (watch->dcdbc->engine) != watch->state)
+ /* XXX emit watch->name */
+
+ outstanding_watch_free (watch);
+}
+
+void
+dconf_dbus_client_subscribe (DConfDBusClient *dcdbc,
+ const gchar *name)
+{
+ OutstandingWatch *watch;
+ DConfEngineMessage dcem;
+
+ watch = outstanding_watch_new (dcdbc, name);
+ dconf_engine_watch (dcdbc->engine, name, &dcem);
+ dconf_settings_backend_send (dcdbc, &dcem, add_match_done, watch);
+ dconf_engine_message_destroy (&dcem);
+}
+
+void
+dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc,
+ const gchar *name)
+{
+ DConfEngineMessage dcem;
+
+ dconf_engine_unwatch (dcdbc->engine, name, &dcem);
+ dconf_settings_backend_send (dcdbc, &dcem, NULL, NULL);
+ dconf_engine_message_destroy (&dcem);
+}
+
+gboolean
+dconf_dbus_client_has_pending (DConfDBusClient *dcdbc)
+{
+ return dcdbc->outstanding != NULL;
+}
+
+static GVariant *
+dconf_settings_backend_service_func (DConfEngineMessage *dcem)
+{
+ g_assert_not_reached ();
+}
+
+DConfDBusClient *
+dconf_dbus_client_new (const gchar *profile,
+ DBusConnection *system,
+ DBusConnection *session)
+{
+ DConfDBusClient *dcdbc;
+
+ dconf_engine_set_service_func (dconf_settings_backend_service_func);
+
+ dcdbc = g_slice_new (DConfDBusClient);
+ dcdbc->engine = dconf_engine_new (profile);
+ dcdbc->system_bus = dbus_connection_ref (system);
+ dcdbc->session_bus = dbus_connection_ref (session);
+ dcdbc->ref_count = 1;
+
+ return dcdbc;
+}
+
+void
+dconf_dbus_client_unref (DConfDBusClient *dcdbc)
+{
+ if (--dcdbc->ref_count == 0)
+ {
+ dbus_connection_unref (dcdbc->session_bus);
+ dbus_connection_unref (dcdbc->system_bus);
+
+ g_slice_free (DConfDBusClient, dcdbc);
+ }
+}
+
+DConfDBusClient *
+dconf_dbus_client_ref (DConfDBusClient *dcdbc)
+{
+ dcdbc->ref_count++;
+
+ return dcdbc;
+}
diff --git a/dbus-1/dconf-dbus-1.h b/dbus-1/dconf-dbus-1.h
new file mode 100644
index 0000000..d137fc8
--- /dev/null
+++ b/dbus-1/dconf-dbus-1.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2010 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the licence, or (at
+ * your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ **/
+
+#ifndef _dconf_dbus_1_h_
+#define _dconf_dbus_1_h_
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef struct _DConfDBusClient DConfDBusClient;
+
+DConfDBusClient * dconf_dbus_client_new (const gchar *profile,
+ DBusConnection *system,
+ DBusConnection *session);
+void dconf_dbus_client_unref (DConfDBusClient *dcdbc);
+DConfDBusClient * dconf_dbus_client_ref (DConfDBusClient *dcdbc);
+
+GVariant * dconf_dbus_client_read (DConfDBusClient *dcdbc,
+ const gchar *key);
+gboolean dconf_dbus_client_write (DConfDBusClient *dcdbc,
+ const gchar *key,
+ GVariant *value);
+void dconf_dbus_client_subscribe (DConfDBusClient *dcdbc,
+ const gchar *name);
+void dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc,
+ const gchar *name);
+gboolean dconf_dbus_client_has_pending (DConfDBusClient *dcdbc);
+
+#endif /* _dconf_dbus_1_h_ */
diff --git a/tests/.gitignore b/tests/.gitignore
index 791498d..e4f23e5 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,2 +1,3 @@
paths
gsettings
+dbus1
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 99f3760..6f14f30 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,8 +1,9 @@
AM_CFLAGS = -std=c89 -Wall -Wmissing-prototypes -Wwrite-strings
-INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/engine -I$(top_srcdir)/client $(gio_CFLAGS)
+INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/engine -I$(top_srcdir)/client $(gio_CFLAGS) -I$(top_srcdir)/dbus-1 $(dbus_CFLAGS)
-noinst_PROGRAMS = paths gsettings
+noinst_PROGRAMS = paths gsettings dbus1
+dbus1_LDFLAGS = -L../dbus-1 -ldconf-dbus-1 $(glib_LIBS)
gsettings_LDADD = $(gio_LIBS)
paths_LDADD = $(gio_LIBS)
paths_SOURCES = \
diff --git a/tests/dbus1.c b/tests/dbus1.c
new file mode 100644
index 0000000..497d26a
--- /dev/null
+++ b/tests/dbus1.c
@@ -0,0 +1,377 @@
+/**
+ * Copyright © 2010 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the licence, or (at
+ * your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ **/
+
+#include <dconf-dbus-1.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+
+static DConfDBusClient *backend;
+
+static void
+free_variant (gpointer data)
+{
+ if (data != NULL)
+ g_variant_unref (data);
+}
+
+static GVariant *
+do_read (const gchar *key)
+{
+ return dconf_dbus_client_read (backend, key);
+}
+
+static gboolean
+do_write (const gchar *key,
+ GVariant *value)
+{
+ return dconf_dbus_client_write (backend, key, value);
+}
+
+static gboolean
+do_write_tree (GTree *tree)
+{
+ g_assert_not_reached ();
+}
+
+static void
+do_sync (void)
+{
+ g_assert_not_reached ();
+}
+
+#define RANDOM_ELEMENT(array) \
+ array[g_test_rand_int_range(0, G_N_ELEMENTS(array))]
+
+static gchar *
+random_key (void)
+{
+ const gchar * const words[] = {
+ "alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf",
+ "hotel", "india", "juliet", "kilo", "lima", "mike", "november",
+ "oscar", "papa", "quebec", "romeo", "sierra", "tango", "uniform",
+ "victor", "whiskey", "xray", "yankee", "zulu"
+ };
+ const gchar *parts[8];
+ gint n, i;
+
+ n = g_test_rand_int_range (2, 8);
+ parts[0] = "";
+ for (i = 1; i < n; i++)
+ parts[i] = RANDOM_ELEMENT (words);
+ parts[n] = NULL;
+
+ return g_strjoinv ("/", (gchar **) parts);
+}
+
+static GVariant *
+random_value (void)
+{
+ switch (g_test_rand_int_range (0, 3))
+ {
+ case 0:
+ return g_variant_new_int32 (g_test_rand_int ());
+
+ case 1:
+ return g_variant_new_boolean (g_test_rand_bit ());
+
+ case 2:
+ {
+ gint length = g_test_rand_int_range (0, 24);
+ gchar buffer[24];
+ gint i;
+
+ for (i = 0; i < length; i++)
+ buffer[i] = 'a' + g_test_rand_int_range (0, 26);
+ buffer[i] = '\0';
+
+ return g_variant_new_string (buffer);
+ }
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static GTree *
+random_tree (void)
+{
+ GTree *tree;
+ gint n;
+
+ tree = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
+ g_free, free_variant);
+ n = g_test_rand_int_range (1, 20);
+
+ while (n--)
+ g_tree_insert (tree, random_key (), g_variant_ref_sink (random_value ()));
+
+ return tree;
+}
+
+static void
+apply_change (GHashTable *table,
+ const gchar *key,
+ GVariant *value)
+{
+ if (value)
+ g_hash_table_insert (table, g_strdup (key), g_variant_ref_sink (value));
+ else
+ g_hash_table_insert (table, g_strdup (key), NULL);
+}
+
+static gboolean
+apply_one_change (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ apply_change (user_data, key, value);
+ return FALSE;
+}
+
+static void
+apply_change_tree (GHashTable *table,
+ GTree *tree)
+{
+ g_tree_foreach (tree, apply_one_change, table);
+}
+
+static GHashTable *implicit;
+static GHashTable *explicit;
+
+#if 0
+/* interpose */
+void
+g_settings_backend_changed (GSettingsBackend *backend_,
+ const gchar *key,
+ gpointer origin_tag)
+{
+ GVariant *value;
+
+ /* ensure that we see no dupes from the bus */
+ g_assert (origin_tag == do_write);
+ g_assert (backend == backend_);
+
+ value = do_read (key);
+ apply_change (implicit, key, value);
+ g_variant_unref (value);
+}
+
+/* interpose */
+void
+g_settings_backend_keys_changed (GSettingsBackend *backend_,
+ const gchar *path,
+ const gchar * const *items,
+ gpointer origin_tag)
+{
+ gint i;
+
+ /* ensure that we see no dupes from the bus */
+ g_assert (origin_tag == do_write);
+ g_assert (backend == backend_);
+
+ for (i = 0; items[i]; i++)
+ {
+ GVariant *value;
+ gchar *key;
+
+ key = g_strconcat (path, items[i], NULL);
+ value = do_read (key);
+
+ apply_change (implicit, key, value);
+
+ g_variant_unref (value);
+ g_free (key);
+ }
+}
+#endif
+
+static void
+setup (void)
+{
+ gchar *file;
+
+ file = g_build_filename (g_get_user_config_dir (),
+ "dconf/test", NULL);
+ unlink (file);
+ g_free (file);
+
+ g_setenv ("DCONF_PROFILE", "test", false);
+
+ backend = dconf_dbus_client_new ("test",
+ dbus_bus_get (DBUS_BUS_SYSTEM, 0),
+ dbus_bus_get (DBUS_BUS_SESSION, 0));
+ dconf_dbus_client_subscribe (backend, "/");
+
+ implicit = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, free_variant);
+ explicit = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, free_variant);
+
+ sleep(1);
+}
+
+static void
+make_random_change (void)
+{
+ if (1)
+ {
+ GVariant *value;
+ gchar *key;
+
+ key = random_key ();
+ value = random_value ();
+ apply_change (explicit, key, value);
+ do_write (key, value);
+
+ g_free (key);
+ }
+ else
+ {
+ GTree *tree;
+
+ tree = random_tree ();
+ apply_change_tree (explicit, tree);
+ do_write_tree (tree);
+
+ g_tree_unref (tree);
+ }
+}
+
+guint64 dconf_time;
+guint64 ghash_time;
+guint64 lookups;
+gboolean dots;
+
+static void
+verify_consistency (void)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (dots)
+ g_print (".");
+ else
+ g_print ("(%d)", g_hash_table_size (explicit));
+
+ /*g_assert (g_hash_table_size (explicit) == g_hash_table_size
+ * (implicit)); */
+ g_hash_table_iter_init (&iter, explicit);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ if (value)
+ {
+ GVariant *other;
+
+ dconf_time -= g_get_monotonic_time ();
+ other = do_read (key);
+ dconf_time += g_get_monotonic_time ();
+ g_assert (g_variant_equal (value, other));
+ g_variant_unref (other);
+ }
+ else
+ {
+ g_assert (g_hash_table_lookup (implicit, key) == NULL);
+ g_assert (do_read (key) == NULL);
+ }
+
+ lookups++;
+ }
+}
+
+#if 0
+static void
+dump_table (void)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_print ("{");
+ g_hash_table_iter_init (&iter, explicit);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ if (value)
+ {
+ gchar *printed;
+
+ if (value)
+ printed = g_variant_print (value, FALSE);
+ else
+ printed = g_strdup ("None");
+
+ g_print ("'%s': %s, ", (gchar *) key, printed);
+ g_free (printed);
+ }
+ g_print ("}");
+}
+#endif
+
+static void
+test (void)
+{
+ int i;
+
+ g_print ("Testing dconf...");
+ for (i = 0; i < 1000; i++)
+ {
+ g_print (" %d", i);
+ make_random_change ();
+ verify_consistency ();
+ }
+
+ g_print ("\n");
+ g_print ("GSettings lookup time: %f µs/lookup\n",
+ ((double) dconf_time / lookups));
+ g_print ("GHashTable lookup time: %f µs/lookup\n",
+ ((double) ghash_time / lookups));
+
+ dconf_time = 0;
+ ghash_time = 0;
+ lookups = 0;
+
+ g_print ("\nWaiting for dconf-service to catch up...");
+ do_sync ();
+ g_print (" done.\n");
+
+ g_print ("Measuring dconf read performance...");
+ dots = TRUE;
+ for (i = 0; i < 1000; i++)
+ verify_consistency ();
+ g_print ("\n");
+
+ g_print ("dconf lookup time: %f µs/lookup\n",
+ ((double) dconf_time / lookups));
+ g_print ("GHashTable lookup time: %f µs/lookup\n",
+ ((double) ghash_time / lookups));
+
+ g_hash_table_unref (explicit);
+ g_hash_table_unref (implicit);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ setup ();
+
+ test ();
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]