[dconf] Add torture-test for GSettings backend



commit cc4e8bbf45af0e5d5ae35585778aebec35e261c5
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Dec 19 02:32:23 2010 -0500

    Add torture-test for GSettings backend

 tests/.gitignore  |    1 +
 tests/Makefile.am |    3 +-
 tests/gsettings.c |  393 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 396 insertions(+), 1 deletions(-)
---
diff --git a/tests/.gitignore b/tests/.gitignore
index 217c33c..791498d 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1 +1,2 @@
 paths
+gsettings
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6418598..99f3760 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)
 
-noinst_PROGRAMS = paths
+noinst_PROGRAMS = paths gsettings
 
+gsettings_LDADD = $(gio_LIBS)
 paths_LDADD = $(gio_LIBS)
 paths_SOURCES = \
 	../common/dconf-paths.c		\
diff --git a/tests/gsettings.c b/tests/gsettings.c
new file mode 100644
index 0000000..ce7cf7b
--- /dev/null
+++ b/tests/gsettings.c
@@ -0,0 +1,393 @@
+/**
+ * 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>
+ **/
+
+#define G_SETTINGS_ENABLE_BACKEND
+#include <gio/gsettingsbackend.h>
+#include <gio/gio.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+static GSettingsBackend *backend;
+
+static void
+free_variant (gpointer data)
+{
+  if (data != NULL)
+    g_variant_unref (data);
+}
+
+static GVariant *
+do_read (const gchar *key)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->read (backend, key, NULL, FALSE);
+}
+
+static gboolean
+do_write (const gchar *key,
+          GVariant    *value)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->write (backend, key, value, do_write);
+}
+
+static gboolean
+do_write_tree (GTree *tree)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->write_tree (backend, tree, do_write);
+}
+
+static void
+do_sync (void)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->sync (backend);
+}
+
+#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;
+
+/* interpose */
+void
+g_settings_backend_changed (GSettingsBackend *backend_,
+                            const gchar      *key,
+                            gpointer          origin_tag)
+{
+  /* ensure that we see no dupes from the bus */
+  g_assert (origin_tag == do_write);
+  g_assert (backend == backend_);
+
+  apply_change (implicit, key, do_read (key));
+}
+
+/* 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++)
+    {
+      gchar *key = g_strconcat (path, items[i], NULL);
+      apply_change (implicit, key, do_read (key));
+      g_free (key);
+    }
+}
+
+/* interpose */
+void
+g_settings_backend_changed_tree (GSettingsBackend *backend_,
+                                 GTree            *tree,
+                                 gpointer          origin_tag)
+{
+  const gchar **keys;
+  gchar *path;
+
+  g_settings_backend_flatten_tree (tree, &path, &keys, NULL);
+  g_settings_backend_keys_changed (backend_, path, keys, origin_tag);
+}
+
+static void
+setup (void)
+{
+  extern void _g_io_modules_ensure_loaded (void);
+  GIOExtensionPoint *point;
+  GIOExtension *extension;
+  GType extension_type;
+  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);
+
+  g_type_init ();
+  g_file_new_for_path (".");
+
+  point = g_io_extension_point_lookup ("gsettings-backend");
+  extension = g_io_extension_point_get_extension_by_name (point, "dconf");
+  extension_type = g_io_extension_get_type (extension);
+  backend = g_object_new (extension_type, NULL);
+
+  G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->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 (g_test_rand_bit ())
+    {
+      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 (implicit));
+
+  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;
+
+          ghash_time -= g_get_monotonic_time ();
+          other = g_hash_table_lookup (explicit, key);
+          ghash_time += g_get_monotonic_time ();
+          g_assert (g_variant_equal (value, 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));
+}
+
+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]