service: add support for service-db
[dconf.git] / tests / engine.c
blob62b5b1eeffce2784cf7ccfc3a41277c9b904938c
1 #define _GNU_SOURCE
3 #include "../engine/dconf-engine.h"
4 #include "../engine/dconf-engine-profile.h"
5 #include "dconf-mock.h"
7 #include <glib/gstdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <dlfcn.h>
12 #include <math.h>
14 /* Interpose to catch fopen("/etc/dconf/profile/user") */
15 static const gchar *filename_to_replace;
16 static const gchar *filename_to_replace_it_with;
18 FILE *
19 fopen (const char *filename,
20 const char *mode)
22 static FILE * (*real_fopen) (const char *, const char *);
24 if (!real_fopen)
25 real_fopen = dlsym (RTLD_NEXT, "fopen");
27 if (filename_to_replace && g_str_equal (filename, filename_to_replace))
29 /* Crash if this file was unexpectedly opened */
30 g_assert (filename_to_replace_it_with != NULL);
31 filename = filename_to_replace_it_with;
34 return (* real_fopen) (filename, mode);
37 static GThread *main_thread;
38 static GString *change_log;
40 void
41 dconf_engine_change_notify (DConfEngine *engine,
42 const gchar *prefix,
43 const gchar * const *changes,
44 const gchar *tag,
45 gpointer origin_tag,
46 gpointer user_data)
48 if (change_log)
49 g_string_append_printf (change_log, "%s:%d:%s:%s;",
50 prefix, g_strv_length ((gchar **) changes), changes[0],
51 tag ? tag : "nil");
54 static void
55 verify_and_free (DConfEngineSource **sources,
56 gint n_sources,
57 const gchar * const *expected_names,
58 gint n_expected)
60 gint i;
62 g_assert_cmpint (n_sources, ==, n_expected);
64 g_assert ((sources == NULL) == (n_sources == 0));
66 for (i = 0; i < n_sources; i++)
68 g_assert_cmpstr (sources[i]->name, ==, expected_names[i]);
69 dconf_engine_source_free (sources[i]);
72 g_free (sources);
75 static void
76 test_five_times (const gchar *filename,
77 gint n_expected,
78 ...)
80 const gchar **expected_names;
81 DConfEngineSource **sources;
82 gint n_sources;
83 va_list ap;
84 gint i;
86 expected_names = g_new (const gchar *, n_expected);
87 va_start (ap, n_expected);
88 for (i = 0; i < n_expected; i++)
89 expected_names[i] = va_arg (ap, const gchar *);
90 va_end (ap);
92 /* first try by supplying the profile filename via the API */
93 g_assert (g_getenv ("DCONF_PROFILE") == NULL);
94 g_assert (filename_to_replace == NULL);
95 sources = dconf_engine_profile_open (filename, &n_sources);
96 verify_and_free (sources, n_sources, expected_names, n_expected);
98 /* next try supplying it via the environment */
99 g_setenv ("DCONF_PROFILE", filename, TRUE);
100 g_assert (filename_to_replace == NULL);
101 sources = dconf_engine_profile_open (NULL, &n_sources);
102 verify_and_free (sources, n_sources, expected_names, n_expected);
103 g_unsetenv ("DCONF_PROFILE");
105 /* next try supplying a profile name via API and intercepting fopen */
106 filename_to_replace = "/etc/dconf/profile/myprofile";
107 filename_to_replace_it_with = filename;
108 g_assert (g_getenv ("DCONF_PROFILE") == NULL);
109 sources = dconf_engine_profile_open ("myprofile", &n_sources);
110 verify_and_free (sources, n_sources, expected_names, n_expected);
111 filename_to_replace = NULL;
113 /* next try the same, via the environment */
114 g_setenv ("DCONF_PROFILE", "myprofile", TRUE);
115 filename_to_replace = "/etc/dconf/profile/myprofile";
116 filename_to_replace_it_with = filename;
117 sources = dconf_engine_profile_open (NULL, &n_sources);
118 verify_and_free (sources, n_sources, expected_names, n_expected);
119 g_unsetenv ("DCONF_PROFILE");
120 filename_to_replace = NULL;
122 /* next try to have dconf pick it up as the default user profile */
123 filename_to_replace = "/etc/dconf/profile/user";
124 filename_to_replace_it_with = filename;
125 g_assert (g_getenv ("DCONF_PROFILE") == NULL);
126 sources = dconf_engine_profile_open (NULL, &n_sources);
127 verify_and_free (sources, n_sources, expected_names, n_expected);
128 filename_to_replace = NULL;
130 filename_to_replace_it_with = NULL;
131 g_free (expected_names);
134 static void
135 test_profile_parser (void)
137 DConfEngineSource **sources;
138 gint n_sources;
140 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
142 g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
144 sources = dconf_engine_profile_open (SRCDIR "/profile/this-file-does-not-exist", &n_sources);
145 g_assert_cmpint (n_sources, ==, 0);
146 g_assert (sources == NULL);
147 exit (0);
149 g_test_trap_assert_passed ();
150 g_test_trap_assert_stderr ("*WARNING*: unable to open named profile*");
152 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
154 g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
156 sources = dconf_engine_profile_open (SRCDIR "/profile/broken-profile", &n_sources);
157 g_assert_cmpint (n_sources, ==, 0);
158 g_assert (sources == NULL);
159 exit (0);
161 g_test_trap_assert_passed ();
162 g_test_trap_assert_stderr ("*WARNING*: unknown dconf database*unknown dconf database*");
164 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
166 g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
168 sources = dconf_engine_profile_open (SRCDIR "/profile/gdm", &n_sources);
169 g_assert_cmpint (n_sources, ==, 0);
170 g_assert (sources == NULL);
171 exit (0);
173 g_test_trap_assert_passed ();
174 g_test_trap_assert_stderr ("*WARNING*: unknown dconf database*unknown dconf database*");
176 test_five_times (SRCDIR "/profile/empty-profile", 0);
177 test_five_times (SRCDIR "/profile/test-profile", 1, "test");
178 test_five_times (SRCDIR "/profile/colourful", 4,
179 "user",
180 "other",
181 "verylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongname",
182 "nonewline");
183 test_five_times (SRCDIR "/profile/dos", 2, "user", "site");
184 test_five_times (SRCDIR "/profile/no-newline-longline", 0);
185 test_five_times (SRCDIR "/profile/many-sources", 10,
186 "user", "local", "room", "floor", "building",
187 "site", "region", "division", "country", "global");
189 /* finally, test that we get the default profile if the user profile
190 * file cannot be located and we do not specify another profile.
192 filename_to_replace = "/etc/dconf/profile/user";
193 filename_to_replace_it_with = SRCDIR "/profile/this-file-does-not-exist";
194 g_assert (g_getenv ("DCONF_PROFILE") == NULL);
195 sources = dconf_engine_profile_open (NULL, &n_sources);
196 filename_to_replace = NULL;
197 g_assert_cmpint (n_sources, ==, 1);
198 g_assert_cmpstr (sources[0]->name, ==, "user");
199 dconf_engine_source_free (sources[0]);
200 g_free (sources);
202 dconf_mock_shm_reset ();
205 static gpointer
206 test_signal_threadsafety_worker (gpointer user_data)
208 gint *finished = user_data;
209 gint i;
211 for (i = 0; i < 20000; i++)
213 DConfEngine *engine;
215 engine = dconf_engine_new (NULL, NULL);
216 dconf_engine_unref (engine);
219 g_atomic_int_inc (finished);
221 return NULL;
224 static void
225 test_signal_threadsafety (void)
227 #define N_WORKERS 4
228 GVariant *parameters;
229 gint finished = 0;
230 gint i;
232 parameters = g_variant_new_parsed ("('/test/key', [''], 'tag')");
233 g_variant_ref_sink (parameters);
235 for (i = 0; i < N_WORKERS; i++)
236 g_thread_unref (g_thread_new ("testcase worker", test_signal_threadsafety_worker, &finished));
238 while (g_atomic_int_get (&finished) < N_WORKERS)
239 dconf_engine_handle_dbus_signal (G_BUS_TYPE_SESSION,
240 ":1.2.3",
241 "/ca/desrt/dconf/Writer/user",
242 "Notify", parameters);
243 g_variant_unref (parameters);
245 dconf_mock_shm_reset ();
248 static void
249 test_user_source (void)
251 DConfEngineSource *source;
252 GvdbTable *table;
253 GvdbTable *locks;
254 gboolean reopened;
256 /* Create the source from a clean slate */
257 source = dconf_engine_source_new ("user-db:user");
258 g_assert (source != NULL);
259 g_assert (source->values == NULL);
260 g_assert (source->locks == NULL);
262 /* Refresh it the first time.
263 * This should cause it to open the shm.
264 * FALSE should be returned because there is no database file.
266 reopened = dconf_engine_source_refresh (source);
267 g_assert (!reopened);
268 dconf_mock_shm_assert_log ("open user;");
270 /* Try to refresh it. There must be no IO at this point. */
271 reopened = dconf_engine_source_refresh (source);
272 g_assert (!reopened);
273 dconf_mock_shm_assert_log ("");
275 /* Add a real database. */
276 table = dconf_mock_gvdb_table_new ();
277 dconf_mock_gvdb_table_insert (table, "/values/int32", g_variant_new_int32 (123456), NULL);
278 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", table);
280 /* Try to refresh it again.
281 * Because we didn't flag the change there must still be no IO.
283 reopened = dconf_engine_source_refresh (source);
284 g_assert (!reopened);
285 g_assert (source->values == NULL);
286 g_assert (source->locks == NULL);
287 dconf_mock_shm_assert_log ("");
289 /* Now flag it and reopen. */
290 dconf_mock_shm_flag ("user");
291 reopened = dconf_engine_source_refresh (source);
292 g_assert (reopened);
293 g_assert (source->values != NULL);
294 g_assert (source->locks == NULL);
295 g_assert (gvdb_table_has_value (source->values, "/values/int32"));
296 dconf_mock_shm_assert_log ("close;open user;");
298 /* Do it again -- should get the same result, after some IO */
299 dconf_mock_shm_flag ("user");
300 reopened = dconf_engine_source_refresh (source);
301 g_assert (reopened);
302 g_assert (source->values != NULL);
303 g_assert (source->locks == NULL);
304 dconf_mock_shm_assert_log ("close;open user;");
306 /* "Delete" the gvdb and make sure dconf notices after a flag */
307 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", NULL);
308 dconf_mock_shm_flag ("user");
309 reopened = dconf_engine_source_refresh (source);
310 g_assert (reopened);
311 g_assert (source->values == NULL);
312 g_assert (source->locks == NULL);
313 dconf_mock_shm_assert_log ("close;open user;");
315 /* Add a gvdb with a lock */
316 table = dconf_mock_gvdb_table_new ();
317 locks = dconf_mock_gvdb_table_new ();
318 dconf_mock_gvdb_table_insert (table, "/values/int32", g_variant_new_int32 (123456), NULL);
319 dconf_mock_gvdb_table_insert (locks, "/values/int32", g_variant_new_boolean (TRUE), NULL);
320 dconf_mock_gvdb_table_insert (table, ".locks", NULL, locks);
321 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", table);
323 /* Reopen and check if we have the lock */
324 dconf_mock_shm_flag ("user");
325 reopened = dconf_engine_source_refresh (source);
326 g_assert (reopened);
327 g_assert (source->values != NULL);
328 g_assert (source->locks != NULL);
329 g_assert (gvdb_table_has_value (source->values, "/values/int32"));
330 g_assert (gvdb_table_has_value (source->locks, "/values/int32"));
331 dconf_mock_shm_assert_log ("close;open user;");
333 /* Reopen one last time */
334 dconf_mock_shm_flag ("user");
335 reopened = dconf_engine_source_refresh (source);
336 g_assert (reopened);
337 g_assert (source->values != NULL);
338 g_assert (source->locks != NULL);
339 dconf_mock_shm_assert_log ("close;open user;");
341 dconf_engine_source_free (source);
342 dconf_mock_shm_assert_log ("close;");
344 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", NULL);
345 dconf_mock_shm_reset ();
348 static void
349 test_system_source (void)
351 DConfEngineSource *source;
352 GvdbTable *first_table;
353 GvdbTable *next_table;
354 gboolean reopened;
356 source = dconf_engine_source_new ("system-db:site");
357 g_assert (source != NULL);
359 /* Check to see that we get the warning about the missing file. */
360 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
362 g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
364 /* Failing to open should return FALSE from refresh */
365 reopened = dconf_engine_source_refresh (source);
366 g_assert (!reopened);
367 g_assert (source->values == NULL);
369 /* Attempt the reopen to make sure we don't get two warnings.
370 * We should see FALSE again since we go from NULL to NULL.
372 reopened = dconf_engine_source_refresh (source);
373 g_assert (!reopened);
375 /* Create the file after the fact and make sure it opens properly */
376 first_table = dconf_mock_gvdb_table_new ();
377 dconf_mock_gvdb_install ("/etc/dconf/db/site", first_table);
379 reopened = dconf_engine_source_refresh (source);
380 g_assert (reopened);
381 g_assert (source->values != NULL);
383 dconf_engine_source_free (source);
385 exit (0);
387 g_test_trap_assert_passed ();
388 /* Check that we only saw the warning, but only one time. */
389 g_test_trap_assert_stderr ("*this gvdb does not exist; expect degraded performance*");
390 g_test_trap_assert_stderr_unmatched ("*degraded*degraded*");
392 /* Create the file before the first refresh attempt */
393 first_table = dconf_mock_gvdb_table_new ();
394 dconf_mock_gvdb_install ("/etc/dconf/db/site", first_table);
395 /* Hang on to a copy for ourselves for below... */
396 dconf_mock_gvdb_table_ref (first_table);
398 /* See that we get the database. */
399 reopened = dconf_engine_source_refresh (source);
400 g_assert (reopened);
401 g_assert (source->values == first_table);
403 /* Do a refresh, make sure there is no change. */
404 reopened = dconf_engine_source_refresh (source);
405 g_assert (!reopened);
406 g_assert (source->values == first_table);
408 /* Replace the table on "disk" but don't invalidate the old one */
409 next_table = dconf_mock_gvdb_table_new ();
410 dconf_mock_gvdb_install ("/etc/dconf/db/site", next_table);
412 /* Make sure the old table remains open (ie: no IO performed) */
413 reopened = dconf_engine_source_refresh (source);
414 g_assert (!reopened);
415 g_assert (source->values == first_table);
417 /* Now mark the first table invalid and reopen */
418 dconf_mock_gvdb_table_invalidate (first_table);
419 gvdb_table_free (first_table);
420 reopened = dconf_engine_source_refresh (source);
421 g_assert (reopened);
422 g_assert (source->values == next_table);
424 /* Remove the file entirely and do the same thing */
425 dconf_mock_gvdb_install ("/etc/dconf/db/site", NULL);
426 reopened = dconf_engine_source_refresh (source);
427 g_assert (!reopened);
429 dconf_engine_source_free (source);
432 static void
433 invalidate_state (guint n_sources,
434 guint source_types,
435 gpointer *state)
437 gint i;
439 for (i = 0; i < n_sources; i++)
440 if (source_types & (1u << i))
442 if (state[i])
444 dconf_mock_gvdb_table_invalidate (state[i]);
445 gvdb_table_free (state[i]);
448 else
450 dconf_mock_shm_flag (state[i]);
451 g_free (state[i]);
455 static void
456 setup_state (guint n_sources,
457 guint source_types,
458 guint database_state,
459 gpointer *state)
461 gint i;
463 for (i = 0; i < n_sources; i++)
465 guint contents = database_state % 7;
466 GvdbTable *table = NULL;
467 gchar *filename;
469 if (contents)
471 table = dconf_mock_gvdb_table_new ();
473 /* Even numbers get the value setup */
474 if ((contents & 1) == 0)
475 dconf_mock_gvdb_table_insert (table, "/value", g_variant_new_uint32 (i), NULL);
477 /* Numbers above 2 get the locks table */
478 if (contents > 2)
480 GvdbTable *locks;
482 locks = dconf_mock_gvdb_table_new ();
484 /* Numbers above 4 get the lock set */
485 if (contents > 4)
486 dconf_mock_gvdb_table_insert (locks, "/value", g_variant_new_boolean (TRUE), NULL);
488 dconf_mock_gvdb_table_insert (table, ".locks", NULL, locks);
492 if (source_types & (1u << i))
494 if (state)
496 if (table)
497 state[i] = dconf_mock_gvdb_table_ref (table);
498 else
499 state[i] = NULL;
502 filename = g_strdup_printf ("/etc/dconf/db/db%d", i);
504 else
506 if (state)
507 state[i] = g_strdup_printf ("db%d", i);
509 filename = g_strdup_printf ("/HOME/.config/dconf/db%d", i);
512 dconf_mock_gvdb_install (filename, table);
513 g_free (filename);
515 database_state /= 7;
519 static void
520 create_profile (const gchar *filename,
521 guint n_sources,
522 guint source_types)
524 GError *error = NULL;
525 GString *profile;
526 gint i;
528 profile = g_string_new (NULL);
529 for (i = 0; i < n_sources; i++)
530 if (source_types & (1u << i))
531 g_string_append_printf (profile, "system-db:db%d\n", i);
532 else
533 g_string_append_printf (profile, "user-db:db%d\n", i);
534 g_file_set_contents (filename, profile->str, profile->len, &error);
535 g_assert_no_error (error);
536 g_string_free (profile, TRUE);
539 static void
540 check_read (DConfEngine *engine,
541 guint n_sources,
542 guint source_types,
543 guint database_state)
545 gboolean any_values = FALSE;
546 gboolean any_locks = FALSE;
547 gint expected = -1;
548 gboolean writable;
549 GVariant *value;
550 gchar **list;
551 guint i;
553 /* The value we expect to read is number of the first source that has
554 * the value set (ie: odd digit in database_state) up to the lowest
555 * level lock.
557 * We go over each database. If 'expected' has not yet been set and
558 * we find that we should have a value in this database, we set it.
559 * If we find that we should have a lock in this database, we unset
560 * any previous values (since they should not have been written).
562 * We initially code this loop in a different way than the one in
563 * dconf itself is currently implemented...
565 * We also take note of if we saw any locks and cross-check that with
566 * dconf_engine_is_writable(). We check if we saw and values at all
567 * and cross-check that with dconf_engine_list() (which ignores
568 * locks).
570 for (i = 0; i < n_sources; i++)
572 guint contents = database_state % 7;
574 /* A lock here should prevent higher reads */
575 if (contents > 4)
577 /* Locks in the first database don't count... */
578 if (i != 0)
579 any_locks = TRUE;
580 expected = -1;
583 /* A value here should be read */
584 if (contents && !(contents & 1) && expected == -1)
586 any_values = TRUE;
587 expected = i;
590 database_state /= 7;
593 value = dconf_engine_read (engine, NULL, "/value");
595 if (expected != -1)
597 g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32));
598 g_assert_cmpint (g_variant_get_uint32 (value), ==, expected);
599 g_variant_unref (value);
601 else
602 g_assert (value == NULL);
604 /* We are writable if the first database is a user database and we
605 * didn't encounter any locks...
607 writable = dconf_engine_is_writable (engine, "/value");
608 g_assert_cmpint (writable, ==, n_sources && !(source_types & 1) && !any_locks);
610 list = dconf_engine_list (engine, "/", NULL);
611 if (any_values)
613 g_assert_cmpstr (list[0], ==, "value");
614 g_assert (list[1] == NULL);
616 else
617 g_assert (list[0] == NULL);
618 g_strfreev (list);
621 static void
622 test_read (void)
624 #define MAX_N_SOURCES 3
625 gpointer state[MAX_N_SOURCES];
626 gchar *profile_filename;
627 GError *error = NULL;
628 DConfEngine *engine;
629 guint i, j, k;
630 guint n;
632 /* Hack to silence warning */
633 if (!g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
635 g_test_trap_assert_passed ();
636 g_test_trap_assert_stderr ("*this gvdb does not exist; expect degraded performance*");
637 return;
639 g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
641 /* Our test strategy is as follows:
643 * We only test a single key name. It is assumed that gvdb is working
644 * properly already so we are only interested in interactions between
645 * multiple databases for a given key name.
647 * The outermost loop is over 'n'. This is how many sources are in
648 * our test. We test 0 to 3 (which should be enough to cover all
649 * 'interesting' possibilities). 4 takes too long to run (2*7*7 ~=
650 * 100 times as long as 3).
652 * The next loop is over 'i'. This goes from 0 to 2^n - 1, with each
653 * bit deciding the type of source of the i-th element
655 * 0: user
657 * 1: system
659 * The next loop is over 'j'. This goes from 0 to 7^n - 1, with each
660 * base-7 digit deciding the state of the database file associated
661 * with the i-th source:
663 * j file has value has ".locks" has lock
664 * ----------------------------------------------------
665 * 0 0 - - -
666 * 1 1 0 0 -
667 * 2 1 1 0 -
668 * 3 1 0 1 0
669 * 4 1 1 1 0
670 * 5 1 0 1 1
671 * 6 1 1 1 1
673 * Where 'file' is if the database file exists, 'has value' is if a
674 * value exists at '/value' within the file, 'has ".locks"' is if
675 * there is a ".locks" subtable and 'has lock' is if there is a lock
676 * for '/value' within that table.
678 * Finally, we loop over 'k' as a state to transition to ('k' works
679 * the same way as 'j').
681 * Once we know 'n' and 'i', we can write a profile file.
683 * Once we know 'j' we can setup the initial state, create the engine
684 * and check that we got the expected value. Then we transition to
685 * state 'k' and make sure everything still works as expected.
687 * Since we want to test all j->k transitions, we do the initial setup
688 * of the engine (according to j) inside of the 'k' loop, since we
689 * need to test all possible transitions from 'j'.
692 /* We need a place to put the profile files we use for this test */
693 close (g_file_open_tmp ("dconf-testcase.XXXXXX", &profile_filename, &error));
694 g_assert_no_error (error);
696 g_setenv ("DCONF_PROFILE", profile_filename, TRUE);
698 for (n = 0; n < MAX_N_SOURCES; n++)
699 for (i = 0; i < pow (2, n); i++)
701 gint n_possible_states = pow (7, n);
703 /* Step 1: write out the profile file */
704 create_profile (profile_filename, n, i);
706 for (j = 0; j < n_possible_states; j++)
707 for (k = 0; k < n_possible_states; k++)
709 guint64 old_state, new_state;
711 /* Step 2: setup the state */
712 setup_state (n, i, j, (j != k) ? state : NULL);
714 /* Step 3: create the engine */
715 engine = dconf_engine_new (NULL, NULL);
717 /* Step 4: read, and check result */
718 check_read (engine, n, i, j);
719 old_state = dconf_engine_get_state (engine);
721 /* Step 5: change to the new state */
722 if (j != k)
724 setup_state (n, i, k, NULL);
725 invalidate_state (n, i, state);
728 /* Step 6: read, and check result */
729 check_read (engine, n, i, k);
730 new_state = dconf_engine_get_state (engine);
732 g_assert ((j == k) == (new_state == old_state));
734 /* Clean up */
735 setup_state (n, i, 0, NULL);
736 dconf_engine_unref (engine);
740 /* Clean up the tempfile we were using... */
741 g_unsetenv ("DCONF_PROFILE");
742 g_unlink (profile_filename);
743 g_free (profile_filename);
744 exit (0);
747 static void
748 test_watch_fast (void)
750 DConfEngine *engine;
751 GvdbTable *table;
752 GVariant *triv;
753 guint64 a, b;
755 change_log = g_string_new (NULL);
757 table = dconf_mock_gvdb_table_new ();
758 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", table);
759 table = dconf_mock_gvdb_table_new ();
760 dconf_mock_gvdb_install ("/etc/dconf/db/site", table);
762 triv = g_variant_ref_sink (g_variant_new ("()"));
764 g_setenv ("DCONF_PROFILE", SRCDIR "/profile/dos", TRUE);
765 engine = dconf_engine_new (NULL, NULL);
766 g_unsetenv ("DCONF_PROFILE");
768 /* Check that establishing a watch works properly in the normal case.
770 a = dconf_engine_get_state (engine);
771 dconf_engine_watch_fast (engine, "/a/b/c");
772 /* watches do not count as outstanding changes */
773 g_assert (!dconf_engine_has_outstanding (engine));
774 dconf_engine_sync (engine);
775 b = dconf_engine_get_state (engine);
776 g_assert_cmpuint (a, ==, b);
777 /* both AddMatch results come back before shm is flagged */
778 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
779 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
780 g_assert (g_queue_is_empty (&dconf_mock_dbus_outstanding_call_handles));
781 dconf_mock_shm_flag ("user");
782 b = dconf_engine_get_state (engine);
783 g_assert_cmpuint (a, !=, b);
784 g_assert_cmpstr (change_log->str, ==, "");
785 dconf_engine_unwatch_fast (engine, "/a/b/c");
786 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
787 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
788 g_assert (g_queue_is_empty (&dconf_mock_dbus_outstanding_call_handles));
790 /* Establish a watch and fail the race. */
791 a = dconf_engine_get_state (engine);
792 dconf_engine_watch_fast (engine, "/a/b/c");
793 g_assert (!dconf_engine_has_outstanding (engine));
794 dconf_engine_sync (engine);
795 b = dconf_engine_get_state (engine);
796 g_assert_cmpuint (a, ==, b);
797 /* one AddMatch result comes back -after- shm is flagged */
798 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
799 dconf_mock_shm_flag ("user");
800 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
801 g_assert (g_queue_is_empty (&dconf_mock_dbus_outstanding_call_handles));
802 b = dconf_engine_get_state (engine);
803 g_assert_cmpuint (a, !=, b);
804 g_assert_cmpstr (change_log->str, ==, "/:1::nil;");
805 dconf_engine_unwatch_fast (engine, "/a/b/c");
806 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
807 dconf_engine_call_handle_reply (g_queue_pop_head (&dconf_mock_dbus_outstanding_call_handles), triv, NULL);
808 g_assert (g_queue_is_empty (&dconf_mock_dbus_outstanding_call_handles));
810 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", NULL);
811 dconf_mock_gvdb_install ("/etc/dconf/db/site", NULL);
812 dconf_engine_unref (engine);
813 g_string_free (change_log, TRUE);
814 change_log = NULL;
815 g_variant_unref (triv);
818 static const gchar *match_request_type;
819 static gboolean got_match_request[5];
821 static GVariant *
822 handle_match_request (GBusType bus_type,
823 const gchar *bus_name,
824 const gchar *object_path,
825 const gchar *interface_name,
826 const gchar *method_name,
827 GVariant *parameters,
828 const GVariantType *expected_type,
829 GError **error)
831 const gchar *match_rule;
833 g_assert_cmpstr (bus_name, ==, "org.freedesktop.DBus");
834 /* any object path works... */
835 g_assert_cmpstr (bus_name, ==, "org.freedesktop.DBus");
836 g_assert_cmpstr (method_name, ==, match_request_type);
837 g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(s)");
838 g_variant_get (parameters, "(&s)", &match_rule);
839 g_assert (strstr (match_rule, "arg0path='/a/b/c'"));
840 g_assert (!got_match_request[bus_type]);
841 got_match_request[bus_type] = TRUE;
843 return g_variant_new ("()");
846 static void
847 test_watch_sync (void)
849 DConfEngine *engine;
851 dconf_mock_dbus_sync_call_handler = handle_match_request;
853 g_setenv ("DCONF_PROFILE", SRCDIR "/profile/dos", TRUE);
854 engine = dconf_engine_new (NULL, NULL);
855 g_unsetenv ("DCONF_PROFILE");
857 match_request_type = "AddMatch";
858 dconf_engine_watch_sync (engine, "/a/b/c");
859 g_assert (got_match_request[G_BUS_TYPE_SESSION]);
860 g_assert (got_match_request[G_BUS_TYPE_SYSTEM]);
861 got_match_request[G_BUS_TYPE_SESSION] = FALSE;
862 got_match_request[G_BUS_TYPE_SYSTEM] = FALSE;
864 match_request_type = "RemoveMatch";
865 dconf_engine_unwatch_sync (engine, "/a/b/c");
866 g_assert (got_match_request[G_BUS_TYPE_SESSION]);
867 g_assert (got_match_request[G_BUS_TYPE_SYSTEM]);
868 got_match_request[G_BUS_TYPE_SESSION] = FALSE;
869 got_match_request[G_BUS_TYPE_SYSTEM] = FALSE;
871 dconf_engine_unref (engine);
873 dconf_mock_dbus_sync_call_handler = NULL;
874 match_request_type = NULL;
878 main (int argc, char **argv)
880 g_setenv ("XDG_CONFIG_HOME", "/HOME/.config", TRUE);
881 g_unsetenv ("DCONF_PROFILE");
883 main_thread = g_thread_self ();
885 g_test_init (&argc, &argv, NULL);
887 g_test_add_func ("/engine/profile-parser", test_profile_parser);
888 g_test_add_func ("/engine/signal-threadsafety", test_signal_threadsafety);
889 g_test_add_func ("/engine/sources/user", test_user_source);
890 g_test_add_func ("/engine/sources/system", test_system_source);
891 g_test_add_func ("/engine/read", test_read);
892 g_test_add_func ("/engine/watch/fast", test_watch_fast);
893 g_test_add_func ("/engine/watch/sync", test_watch_sync);
895 return g_test_run ();