#include "../common/dconf-changeset.h" static gboolean should_not_run (const gchar *key, GVariant *value, gpointer user_data) { g_assert_not_reached (); } static gboolean is_null (const gchar *key, GVariant *value, gpointer user_data) { return value == NULL; } static gboolean is_not_null (const gchar *key, GVariant *value, gpointer user_data) { return value != NULL; } static void test_basic (void) { DConfChangeset *changeset; gboolean result; GVariant *value; gint n_items; changeset = dconf_changeset_new (); dconf_changeset_ref (changeset); dconf_changeset_all (changeset, should_not_run, NULL); n_items = dconf_changeset_describe (changeset, NULL, NULL, NULL); g_assert_cmpint (n_items, ==, 0); dconf_changeset_unref (changeset); dconf_changeset_unref (changeset); changeset = dconf_changeset_new_write ("/value/a", NULL); result = dconf_changeset_all (changeset, is_null, NULL); g_assert (result); result = dconf_changeset_all (changeset, is_not_null, NULL); g_assert (!result); result = dconf_changeset_get (changeset, "/value/a", &value); g_assert (result); g_assert (value == NULL); result = dconf_changeset_get (changeset, "/value/b", &value); g_assert (!result); dconf_changeset_set (changeset, "/value/b", g_variant_new_int32 (123)); result = dconf_changeset_all (changeset, is_null, NULL); g_assert (!result); result = dconf_changeset_all (changeset, is_not_null, NULL); g_assert (!result); result = dconf_changeset_get (changeset, "/value/a", &value); g_assert (result); g_assert (value == NULL); result = dconf_changeset_get (changeset, "/value/b", &value); g_assert (result); g_assert_cmpint (g_variant_get_int32 (value), ==, 123); g_variant_unref (value); dconf_changeset_set (changeset, "/value/a", g_variant_new_string ("a string")); result = dconf_changeset_all (changeset, is_null, NULL); g_assert (!result); result = dconf_changeset_all (changeset, is_not_null, NULL); g_assert (result); result = dconf_changeset_get (changeset, "/value/a", &value); g_assert (result); g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "a string"); g_variant_unref (value); result = dconf_changeset_get (changeset, "/value/b", &value); g_assert (result); g_assert_cmpint (g_variant_get_int32 (value), ==, 123); g_variant_unref (value); dconf_changeset_unref (changeset); } static void test_similarity (void) { DConfChangeset *a, *b; a = dconf_changeset_new (); b = dconf_changeset_new (); g_assert (dconf_changeset_is_similar_to (a, b)); g_assert (dconf_changeset_is_similar_to (b, a)); dconf_changeset_set (a, "/value/a", g_variant_new_int32 (0)); g_assert (!dconf_changeset_is_similar_to (a, b)); g_assert (!dconf_changeset_is_similar_to (b, a)); /* different values for the same key are still the same */ dconf_changeset_set (b, "/value/a", g_variant_new_int32 (1)); g_assert (dconf_changeset_is_similar_to (a, b)); g_assert (dconf_changeset_is_similar_to (b, a)); /* make sure even a NULL is counted as different */ dconf_changeset_set (a, "/value/b", NULL); g_assert (!dconf_changeset_is_similar_to (a, b)); g_assert (!dconf_changeset_is_similar_to (b, a)); dconf_changeset_set (b, "/value/b", NULL); g_assert (dconf_changeset_is_similar_to (a, b)); g_assert (dconf_changeset_is_similar_to (b, a)); /* different types are still the same */ dconf_changeset_set (b, "/value/a", g_variant_new_uint32 (222)); g_assert (dconf_changeset_is_similar_to (a, b)); g_assert (dconf_changeset_is_similar_to (b, a)); dconf_changeset_set (a, "/value/c", NULL); dconf_changeset_set (b, "/value/d", NULL); g_assert (!dconf_changeset_is_similar_to (a, b)); g_assert (!dconf_changeset_is_similar_to (b, a)); dconf_changeset_unref (a); dconf_changeset_unref (b); } static void test_describe (void) { DConfChangeset *changeset; const gchar * const *keys; GVariant * const *values; const gchar *prefix; gint n_items; gint i; /* test zero items */ changeset = dconf_changeset_new (); n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 0); dconf_changeset_unref (changeset); /* test one NULL item */ changeset = dconf_changeset_new_write ("/value/a", NULL); n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 1); g_assert_cmpstr (prefix, ==, "/value/a"); g_assert_cmpstr (keys[0], ==, ""); g_assert (keys[1] == NULL); g_assert (values[0] == NULL); /* Check again */ prefix = NULL; keys = NULL; values = NULL; n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 1); g_assert_cmpstr (prefix, ==, "/value/a"); g_assert_cmpstr (keys[0], ==, ""); g_assert (keys[1] == NULL); g_assert (values[0] == NULL); dconf_changeset_unref (changeset); /* test one non-NULL item */ changeset = dconf_changeset_new_write ("/value/a", g_variant_new_int32 (55)); n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 1); g_assert_cmpstr (prefix, ==, "/value/a"); g_assert_cmpstr (keys[0], ==, ""); g_assert (keys[1] == NULL); g_assert_cmpint (g_variant_get_int32 (values[0]), ==, 55); dconf_changeset_unref (changeset); /* test many items */ changeset = dconf_changeset_new (); for (i = 0; i < 100; i++) { gchar key[80]; g_snprintf (key, sizeof key, "/test/value/%2d", i); dconf_changeset_set (changeset, key, g_variant_new_int32 (i)); } n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, i); g_assert_cmpstr (prefix, ==, "/test/value/"); for (i = 0; i < 100; i++) { gchar key[80]; g_snprintf (key, sizeof key, "%2d", i); g_assert_cmpstr (keys[i], ==, key); g_assert_cmpint (g_variant_get_int32 (values[i]), ==, i); } g_assert (keys[n_items] == NULL); dconf_changeset_unref (changeset); /* test many items with common names */ changeset = dconf_changeset_new (); for (i = 0; i < 100; i++) { gchar key[80]; g_snprintf (key, sizeof key, "/test/value/aaa%02d", i); dconf_changeset_set (changeset, key, g_variant_new_int32 (i)); } n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, i); g_assert_cmpstr (prefix, ==, "/test/value/"); for (i = 0; i < 100; i++) { gchar key[80]; g_snprintf (key, sizeof key, "aaa%02d", i); g_assert_cmpstr (keys[i], ==, key); g_assert_cmpint (g_variant_get_int32 (values[i]), ==, i); } g_assert (keys[n_items] == NULL); dconf_changeset_unref (changeset); /* test several values in different directories */ changeset = dconf_changeset_new (); dconf_changeset_set (changeset, "/value/reset/", NULL); dconf_changeset_set (changeset, "/value/int/a", g_variant_new_int32 (123)); dconf_changeset_set (changeset, "/value/string", g_variant_new_string ("bar")); dconf_changeset_set (changeset, "/value/string/a", g_variant_new_string ("foo")); n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 4); g_assert_cmpstr (prefix, ==, "/value/"); g_assert_cmpstr (keys[0], ==, "int/a"); g_assert_cmpint (g_variant_get_int32 (values[0]), ==, 123); g_assert_cmpstr (keys[1], ==, "reset/"); g_assert (values[1] == NULL); g_assert_cmpstr (keys[2], ==, "string"); g_assert_cmpstr (g_variant_get_string (values[2], NULL), ==, "bar"); g_assert_cmpstr (keys[3], ==, "string/a"); g_assert_cmpstr (g_variant_get_string (values[3], NULL), ==, "foo"); g_assert (keys[4] == NULL); dconf_changeset_unref (changeset); /* test a couple of values in very different directories */ changeset = dconf_changeset_new_write ("/a/deep/directory/", NULL); dconf_changeset_set (changeset, "/another/deep/directory/", NULL); n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 2); g_assert_cmpstr (prefix, ==, "/"); g_assert_cmpstr (keys[0], ==, "a/deep/directory/"); g_assert_cmpstr (keys[1], ==, "another/deep/directory/"); g_assert (keys[2] == NULL); g_assert (values[0] == NULL); g_assert (values[1] == NULL); dconf_changeset_unref (changeset); /* one more similar case, but with the first letter different */ changeset = dconf_changeset_new_write ("/deep/directory/", NULL); dconf_changeset_set (changeset, "/another/deep/directory/", NULL); n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values); g_assert_cmpint (n_items, ==, 2); g_assert_cmpstr (prefix, ==, "/"); g_assert_cmpstr (keys[0], ==, "another/deep/directory/"); g_assert_cmpstr (keys[1], ==, "deep/directory/"); g_assert (keys[2] == NULL); g_assert (values[0] == NULL); g_assert (values[1] == NULL); dconf_changeset_unref (changeset); } static void test_reset (void) { DConfChangeset *changeset; GVariant *value; changeset = dconf_changeset_new (); g_assert (!dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (!dconf_changeset_get (changeset, "/value/a", &value)); /* value was not set */ /* set a value */ dconf_changeset_set (changeset, "/value/a", g_variant_new_boolean (TRUE)); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value != NULL); g_variant_unref (value); /* record the reset */ dconf_changeset_set (changeset, "/value/", NULL); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value == NULL); /* write it back */ dconf_changeset_set (changeset, "/value/a", g_variant_new_boolean (TRUE)); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value != NULL); g_variant_unref (value); /* reset again */ dconf_changeset_set (changeset, "/value/", NULL); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value == NULL); /* write again */ dconf_changeset_set (changeset, "/value/a", g_variant_new_boolean (TRUE)); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value != NULL); g_variant_unref (value); /* reset a different way */ dconf_changeset_set (changeset, "/value/a", NULL); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value == NULL); /* write last time */ dconf_changeset_set (changeset, "/value/a", g_variant_new_boolean (TRUE)); g_assert (dconf_changeset_get (changeset, "/value/a", NULL)); g_assert (dconf_changeset_get (changeset, "/value/a", &value)); g_assert (value != NULL); g_variant_unref (value); dconf_changeset_unref (changeset); } static gboolean has_same_value (const gchar *key, GVariant *value, gpointer user_data) { DConfChangeset *other = user_data; GVariant *other_value; gboolean success; success = dconf_changeset_get (other, key, &other_value); g_assert (success); if (value == NULL) g_assert (other_value == NULL); else { g_assert (g_variant_equal (value, other_value)); g_variant_unref (other_value); } return TRUE; } static void test_serialisation (DConfChangeset *changes) { GVariant *serialised; DConfChangeset *copy; serialised = dconf_changeset_serialise (changes); copy = dconf_changeset_deserialise (serialised); g_variant_unref (serialised); g_assert (dconf_changeset_is_similar_to (copy, changes)); g_assert (dconf_changeset_is_similar_to (changes, copy)); g_assert (dconf_changeset_all (copy, has_same_value, changes)); g_assert (dconf_changeset_all (changes, has_same_value, copy)); dconf_changeset_unref (copy); } static void test_serialiser (void) { DConfChangeset *changeset; changeset = dconf_changeset_new (); test_serialisation (changeset); dconf_changeset_set (changeset, "/some/value", g_variant_new_int32 (333)); test_serialisation (changeset); dconf_changeset_set (changeset, "/other/value", NULL); test_serialisation (changeset); dconf_changeset_set (changeset, "/other/value", g_variant_new_int32 (55)); test_serialisation (changeset); dconf_changeset_set (changeset, "/other/", NULL); test_serialisation (changeset); dconf_changeset_set (changeset, "/", NULL); test_serialisation (changeset); dconf_changeset_unref (changeset); } static void test_change (void) { DConfChangeset *deltaa, *deltab; DConfChangeset *dba, *dbb; dba = dconf_changeset_new_database (NULL); dbb = dconf_changeset_new_database (dba); g_assert (dconf_changeset_is_empty (dbb)); dconf_changeset_unref (dbb); deltaa = dconf_changeset_new (); dconf_changeset_change (dba, deltaa); g_assert (dconf_changeset_is_empty (dba)); dconf_changeset_unref (deltaa); deltaa = dconf_changeset_new_write ("/some/value", NULL); dconf_changeset_change (dba, deltaa); g_assert (dconf_changeset_is_empty (dba)); dconf_changeset_unref (deltaa); deltaa = dconf_changeset_new (); deltab = dconf_changeset_new_write ("/some/value", g_variant_new_int32 (123)); dconf_changeset_change (deltaa, deltab); g_assert (!dconf_changeset_is_empty (deltaa)); dconf_changeset_change (dba, deltab); g_assert (!dconf_changeset_is_empty (dba)); dconf_changeset_unref (deltaa); dconf_changeset_unref (deltab); deltaa = dconf_changeset_new (); deltab = dconf_changeset_new_write ("/other/value", g_variant_new_int32 (123)); dconf_changeset_change (deltaa, deltab); g_assert (!dconf_changeset_is_empty (deltaa)); dconf_changeset_unref (deltab); deltab = dconf_changeset_new_write ("/other/", NULL); dconf_changeset_change (deltaa, deltab); g_assert (!dconf_changeset_is_empty (deltaa)); dconf_changeset_change (dba, deltaa); g_assert (!dconf_changeset_is_empty (dba)); dbb = dconf_changeset_new_database (dba); g_assert (!dconf_changeset_is_empty (dbb)); dconf_changeset_set (dba, "/some/", NULL); dconf_changeset_set (dba, "/other/value", g_variant_new_int32 (123)); g_assert (!dconf_changeset_is_empty (dba)); dconf_changeset_change (dba, deltaa); g_assert (dconf_changeset_is_empty (dba)); g_assert (!dconf_changeset_is_empty (dbb)); dconf_changeset_unref (deltaa); dconf_changeset_unref (deltab); dconf_changeset_unref (dbb); dconf_changeset_unref (dba); } static void assert_diff_change_invariant (DConfChangeset *from, DConfChangeset *to) { DConfChangeset *copy; DConfChangeset *diff; /* Verify this promise from the docs: * * Applying the returned changeset to @from using * dconf_changeset_change() will result in the two changesets being * equal. */ copy = dconf_changeset_new_database (from); diff = dconf_changeset_diff (from, to); if (diff) { dconf_changeset_change (copy, diff); dconf_changeset_unref (diff); } /* Make sure they are now equal */ diff = dconf_changeset_diff (copy, to); g_assert (diff == NULL); /* Why not try it the other way too? */ diff = dconf_changeset_diff (to, copy); g_assert (diff == NULL); dconf_changeset_unref (copy); } static gchar * create_random_key (void) { GString *key; gint i, n; key = g_string_new (NULL); n = g_test_rand_int_range (1, 5); for (i = 0; i < n; i++) { gint j; g_string_append_c (key, '/'); for (j = 0; j < 5; j++) g_string_append_c (key, g_test_rand_int_range ('a', 'z' + 1)); } return g_string_free (key, FALSE); } static GVariant * create_random_value (void) { return g_variant_new_take_string (create_random_key ()); } static DConfChangeset * create_random_db (void) { DConfChangeset *set; gint i, n; set = dconf_changeset_new_database (NULL); n = g_test_rand_int_range (0, 20); for (i = 0; i < n; i++) { GVariant *value = create_random_value (); gchar *key = create_random_key (); dconf_changeset_set (set, key, value); g_free (key); } return set; } static void test_diff (void) { DConfChangeset *a, *b; gint i; /* Check diff between two empties */ a = dconf_changeset_new_database (NULL); b = dconf_changeset_new_database (NULL); assert_diff_change_invariant (a, b); dconf_changeset_unref (a); dconf_changeset_unref (b); /* Check diff between two non-empties that are equal */ a = create_random_db (); b = dconf_changeset_new_database (a); assert_diff_change_invariant (a, b); dconf_changeset_unref (a); dconf_changeset_unref (b); /* Check diff between two random databases that are probably unequal */ for (i = 0; i < 1000; i++) { a = create_random_db (); b = create_random_db (); assert_diff_change_invariant (a, b); dconf_changeset_unref (a); dconf_changeset_unref (b); } } static DConfChangeset * changeset_from_string (const gchar *string, gboolean is_database) { GVariant *parsed; DConfChangeset *changes, *parsed_changes; if (is_database) changes = dconf_changeset_new_database (NULL); else changes = dconf_changeset_new (); if (string != NULL) { parsed = g_variant_parse (NULL, string, NULL, NULL, NULL); parsed_changes = dconf_changeset_deserialise (parsed); dconf_changeset_change (changes, parsed_changes); dconf_changeset_unref (parsed_changes); g_variant_unref (parsed); } return changes; } static gchar * string_from_changeset (DConfChangeset *changeset) { GVariant *serialised; gchar *string; if (dconf_changeset_is_empty (changeset)) return NULL; serialised = dconf_changeset_serialise (changeset); string = g_variant_print (serialised, TRUE); g_variant_unref (serialised); return string; } static void call_filter_changes (const gchar *base_string, const gchar *changes_string, const gchar *expected) { DConfChangeset *base, *changes, *filtered; gchar *filtered_string = NULL; base = changeset_from_string (base_string, TRUE); changes = changeset_from_string (changes_string, FALSE); filtered = dconf_changeset_filter_changes (base, changes); if (filtered != NULL) { filtered_string = string_from_changeset (filtered); dconf_changeset_unref (filtered); } g_assert_cmpstr (filtered_string, ==, expected); dconf_changeset_unref (base); dconf_changeset_unref (changes); g_free (filtered_string); } static void test_filter_changes (void) { /* These tests are mostly negative, since dconf_changeset_filter_changes * is called from dconf_changeset_diff */ // Define test changesets as serialised g_variant strings const gchar *empty = NULL; const gchar *a1 = "{'/a': @mv <'value1'>}"; const gchar *a2 = "{'/a': @mv <'value2'>}"; const gchar *b2 = "{'/b': @mv <'value2'>}"; const gchar *a1b1 = "{'/a': @mv <'value1'>, '/b': @mv <'value1'>}"; const gchar *a1b2 = "{'/a': @mv <'value1'>, '/b': @mv <'value2'>}"; const gchar *a1r1 = "{'/a': @mv <'value1'>, '/r/c': @mv <'value3'>}"; const gchar *key_reset = "{'/a': @mv nothing}"; const gchar *root_reset = "{'/': @mv nothing}"; const gchar *partial_reset = "{'/r/': @mv nothing}"; /* an empty changeset would not change an empty database */ call_filter_changes (empty, empty, NULL); /* an empty changeset would not change a database with values */ call_filter_changes (a1, empty, NULL); /* a changeset would not change a database with the same values */ call_filter_changes (a1, a1, NULL); call_filter_changes (a1b2, a1b2, NULL); /* A non-empty changeset would change an empty database */ call_filter_changes (empty, a1, a1); /* a changeset would change a database with the same keys but * different values */ call_filter_changes (a1, a2, a2); call_filter_changes (a1b1, a1b2, b2); /* A changeset would change a database with disjoint values */ call_filter_changes (a1, b2, b2); /* A changeset would change a database with some equal and some new * values */ call_filter_changes (a1, a1b2, b2); /* A changeset would not change a database with some equal and some * new values */ call_filter_changes (a1b2, a1, NULL); /* A root reset has an effect on a database with values */ call_filter_changes (a1, root_reset, root_reset); call_filter_changes (a1b2, root_reset, root_reset); /* A root reset would have no effect on an empty database */ call_filter_changes (empty, root_reset, NULL); /* A key reset would have no effect on an empty database */ call_filter_changes (empty, key_reset, NULL); /* A key reset would have no effect on a database with other keys */ call_filter_changes (b2, key_reset, NULL); /* A key reset would have an effect on a database containing that * key */ call_filter_changes (a1, key_reset, key_reset); call_filter_changes (a1b1, key_reset, key_reset); /* A partial reset would have no effect on an empty database */ call_filter_changes (empty, partial_reset, NULL); /* A partial reset would have no effect on a database with other * values */ call_filter_changes (a1, partial_reset, NULL); /* A partial reset would have an effect on a database with some values * under that path */ call_filter_changes (a1r1, partial_reset, partial_reset); } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/changeset/basic", test_basic); g_test_add_func ("/changeset/similarity", test_similarity); g_test_add_func ("/changeset/describe", test_describe); g_test_add_func ("/changeset/reset", test_reset); g_test_add_func ("/changeset/serialiser", test_serialiser); g_test_add_func ("/changeset/change", test_change); g_test_add_func ("/changeset/diff", test_diff); g_test_add_func ("/changeset/filter", test_filter_changes); return g_test_run (); }