/** * 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 . * * Author: Ryan Lortie **/ #define G_SETTINGS_ENABLE_BACKEND #include #include #include #include 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) { 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); } } /* 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 (); /* Cause GIO modules to be loaded... */ g_object_unref (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 (implicit, 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)); 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 (); }