1 #define _DEFAULT_SOURCE
2 #include "../client/dconf-client.h"
3 #include "../engine/dconf-engine.h"
4 #include "dconf-mock.h"
5 #include <string.h>
6 #include <stdlib.h>
7 
8 static GThread *main_thread;
9 
10 static void
test_lifecycle(void)11 test_lifecycle (void)
12 {
13   DConfClient *client;
14   GWeakRef weak;
15 
16   client = dconf_client_new ();
17   g_weak_ref_init (&weak, client);
18   g_object_unref (client);
19 
20   g_assert (g_weak_ref_get (&weak) == NULL);
21   g_weak_ref_clear (&weak);
22 }
23 
24 static gboolean changed_was_called;
25 
26 static void
changed(DConfClient * client,const gchar * prefix,const gchar * const * changes,const gchar * tag,gpointer user_data)27 changed (DConfClient         *client,
28          const gchar         *prefix,
29          const gchar * const *changes,
30          const gchar         *tag,
31          gpointer             user_data)
32 {
33   g_assert (g_thread_self () == main_thread);
34 
35   changed_was_called = TRUE;
36 }
37 
38 static void
check_and_free(GVariant * to_check,GVariant * expected)39 check_and_free (GVariant *to_check,
40                 GVariant *expected)
41 {
42   if (expected)
43     {
44       g_variant_ref_sink (expected);
45       g_assert (to_check);
46 
47       g_assert (g_variant_equal (to_check, expected));
48       g_variant_unref (to_check);
49       g_variant_unref (expected);
50     }
51   else
52     g_assert (to_check == NULL);
53 }
54 
55 static void
queue_up_100_writes(DConfClient * client)56 queue_up_100_writes (DConfClient *client)
57 {
58   gint i;
59 
60   /* We send 100 writes, letting them pile up.
61    * At no time should there be more than one write on the wire.
62    */
63   for (i = 0; i < 100; i++)
64     {
65       changed_was_called = FALSE;
66       dconf_client_write_fast (client, "/test/value", g_variant_new_int32 (i), NULL);
67       g_assert (changed_was_called);
68 
69       /* We should always see the most recently written value. */
70       check_and_free (dconf_client_read (client, "/test/value"), g_variant_new_int32 (i));
71       check_and_free (dconf_client_read_full (client, "/test/value", DCONF_READ_DEFAULT_VALUE, NULL), NULL);
72     }
73 
74   g_assert_cmpint (g_queue_get_length (&dconf_mock_dbus_outstanding_call_handles), ==, 1);
75 }
76 
77 static void
fail_one_call(void)78 fail_one_call (void)
79 {
80   DConfEngineCallHandle *handle;
81   GError *error;
82 
83   error = g_error_new_literal (G_FILE_ERROR, G_FILE_ERROR_NOENT, "--expected error from testcase--");
84   handle = g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles);
85   dconf_engine_call_handle_reply (handle, NULL, error);
86   g_error_free (error);
87 }
88 
89 static GLogWriterOutput
log_writer_cb(GLogLevelFlags log_level,const GLogField * fields,gsize n_fields,gpointer user_data)90 log_writer_cb (GLogLevelFlags   log_level,
91                const GLogField *fields,
92                gsize            n_fields,
93                gpointer         user_data)
94 {
95   gsize i;
96 
97   for (i = 0; i < n_fields; i++)
98     {
99       if (g_strcmp0 (fields[i].key, "MESSAGE") == 0 &&
100           strstr (fields[i].value, "--expected error from testcase--"))
101         return G_LOG_WRITER_HANDLED;
102     }
103 
104   return G_LOG_WRITER_UNHANDLED;
105 }
106 
107 static void
test_fast(void)108 test_fast (void)
109 {
110   DConfClient *client;
111 
112   g_log_set_writer_func (log_writer_cb, NULL, NULL);
113 
114   client = dconf_client_new ();
115   g_signal_connect (client, "changed", G_CALLBACK (changed), NULL);
116 
117   queue_up_100_writes (client);
118 
119   /* Start indicating that the writes failed.
120    *
121    * Because of the pending-merge logic, we should only have had to fail two calls.
122    *
123    * Each time, we should see a change notify.
124    */
125 
126   g_assert_cmpint (g_queue_get_length (&dconf_mock_dbus_outstanding_call_handles), == , 1);
127 
128   changed_was_called = FALSE;
129   fail_one_call ();
130   g_assert (changed_was_called);
131 
132   /* For the first failure, we should continue to see the most recently written value (99) */
133   check_and_free (dconf_client_read (client, "/test/value"), g_variant_new_int32 (99));
134   check_and_free (dconf_client_read_full (client, "/test/value", DCONF_READ_DEFAULT_VALUE, NULL), NULL);
135 
136   g_assert_cmpint (g_queue_get_length (&dconf_mock_dbus_outstanding_call_handles), == , 1);
137 
138   changed_was_called = FALSE;
139   fail_one_call ();
140   g_assert (changed_was_called);
141 
142   /* Should read back now as NULL */
143   check_and_free (dconf_client_read (client, "/test/value"), NULL);
144   check_and_free (dconf_client_read_full (client, "/test/value", DCONF_READ_DEFAULT_VALUE, NULL), NULL);
145 
146   g_assert_cmpint (g_queue_get_length (&dconf_mock_dbus_outstanding_call_handles), == , 0);
147 
148   /* Cleanup */
149   g_signal_handlers_disconnect_by_func (client, changed, NULL);
150   g_object_unref (client);
151 }
152 
153 static gboolean changed_a, changed_b, changed_c;
154 
155 static void
coalesce_changed(DConfClient * client,const gchar * prefix,const gchar * const * changes,const gchar * tag,gpointer user_data)156 coalesce_changed (DConfClient         *client,
157                   const gchar         *prefix,
158                   const gchar * const *changes,
159                   const gchar         *tag,
160                   gpointer             user_data)
161 {
162   changed_a = g_str_equal (prefix, "/test/a") || g_strv_contains (changes, "a");
163   changed_b = g_str_equal (prefix, "/test/b") || g_strv_contains (changes, "b");
164   changed_c = g_str_equal (prefix, "/test/c") || g_strv_contains (changes, "c");
165 }
166 
167 static void
test_coalesce(void)168 test_coalesce (void)
169 {
170   gint i, a, b, c;
171   gboolean should_change_a, should_change_b, should_change_c;
172   g_autoptr(DConfClient) client = NULL;
173 
174   gint changes[][3] = {
175     {1, 0, 0},
176     {1, 1, 1},
177     {0, 1, 1},
178     {0, 0, 1},
179     {0, 0, 0},
180     {1, 0, 0},
181     {1, 0, 0},
182   };
183 
184   client = dconf_client_new ();
185   g_signal_connect (client, "changed", G_CALLBACK (coalesce_changed), NULL);
186 
187   a = b = c = 0;
188 
189   for (i = 0; i != G_N_ELEMENTS (changes); ++i)
190     {
191       g_autoptr(DConfChangeset) changeset = NULL;
192 
193       should_change_a = changes[i][0];
194       should_change_b = changes[i][1];
195       should_change_c = changes[i][2];
196 
197       changeset = dconf_changeset_new ();
198 
199       if (should_change_a)
200         dconf_changeset_set (changeset, "/test/a", g_variant_new_int32 (++a));
201       if (should_change_b)
202         dconf_changeset_set (changeset, "/test/b", g_variant_new_int32 (++b));
203       if (should_change_c)
204         dconf_changeset_set (changeset, "/test/c", g_variant_new_int32 (++c));
205 
206       changed_a = changed_b = changed_c = FALSE;
207 
208       g_assert_true (dconf_client_change_fast (client, changeset, NULL));
209 
210       /* Notifications should be only about keys we have just written. */
211       g_assert_cmpint (should_change_a, ==, changed_a);
212       g_assert_cmpint (should_change_b, ==, changed_b);
213       g_assert_cmpint (should_change_c, ==, changed_c);
214 
215       /* We should see value from the most recent write or NULL if we haven't written it yet. */
216       check_and_free (dconf_client_read (client, "/test/a"), a == 0 ? NULL : g_variant_new_int32 (a));
217       check_and_free (dconf_client_read (client, "/test/b"), b == 0 ? NULL : g_variant_new_int32 (b));
218       check_and_free (dconf_client_read (client, "/test/c"), c == 0 ? NULL : g_variant_new_int32 (c));
219     }
220 
221   dconf_mock_dbus_async_reply (g_variant_new ("(s)", "1"), NULL);
222   dconf_mock_dbus_async_reply (g_variant_new ("(s)", "2"), NULL);
223 
224   /* There should be no more requests since all but first have been
225    * coalesced together. */
226   dconf_mock_dbus_assert_no_async ();
227 
228   /* Cleanup */
229   g_signal_handlers_disconnect_by_func (client, changed, NULL);
230 }
231 
232 int
main(int argc,char ** argv)233 main (int argc, char **argv)
234 {
235   setenv ("DCONF_PROFILE", SRCDIR "/profile/will-never-exist", TRUE);
236 
237   main_thread = g_thread_self ();
238 
239   g_test_init (&argc, &argv, NULL);
240 
241   g_test_add_func ("/client/lifecycle", test_lifecycle);
242   g_test_add_func ("/client/basic-fast", test_fast);
243   g_test_add_func ("/client/coalesce", test_coalesce);
244 
245   return g_test_run ();
246 }
247