3 #define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_36 /* Suppress deprecation warnings */
5 #include "../engine/dconf-engine.h"
6 #include "../engine/dconf-engine-profile.h"
7 #include "../common/dconf-enums.h"
8 #include "dconf-mock.h"
10 #include <glib/gstdio.h>
17 /* Interpose to catch fopen("/etc/dconf/profile/user") */
18 static const gchar
*filename_to_replace
;
19 static const gchar
*filename_to_replace_it_with
;
22 fopen (const char *filename
,
25 static FILE * (*real_fopen
) (const char *, const char *);
28 real_fopen
= dlsym (RTLD_NEXT
, "fopen");
30 if (filename_to_replace
&& g_str_equal (filename
, filename_to_replace
))
32 /* Crash if this file was unexpectedly opened */
33 g_assert (filename_to_replace_it_with
!= NULL
);
34 filename
= filename_to_replace_it_with
;
37 return (* real_fopen
) (filename
, mode
);
40 static GThread
*main_thread
;
41 static GString
*change_log
;
44 dconf_engine_change_notify (DConfEngine
*engine
,
46 const gchar
* const *changes
,
48 gboolean is_writability
,
58 g_string_append (change_log
, "w:");
60 joined
= g_strjoinv (",", (gchar
**) changes
);
61 g_string_append_printf (change_log
, "%s:%d:%s:%s;",
62 prefix
, g_strv_length ((gchar
**) changes
), joined
,
68 verify_and_free (DConfEngineSource
**sources
,
70 const gchar
* const *expected_names
,
75 g_assert_cmpint (n_sources
, ==, n_expected
);
77 g_assert ((sources
== NULL
) == (n_sources
== 0));
79 for (i
= 0; i
< n_sources
; i
++)
81 g_assert_cmpstr (sources
[i
]->name
, ==, expected_names
[i
]);
82 dconf_engine_source_free (sources
[i
]);
89 test_five_times (const gchar
*filename
,
93 const gchar
**expected_names
;
94 DConfEngineSource
**sources
;
99 expected_names
= g_new (const gchar
*, n_expected
);
100 va_start (ap
, n_expected
);
101 for (i
= 0; i
< n_expected
; i
++)
102 expected_names
[i
] = va_arg (ap
, const gchar
*);
105 /* first try by supplying the profile filename via the API */
106 g_assert (g_getenv ("DCONF_PROFILE") == NULL
);
107 g_assert (filename_to_replace
== NULL
);
108 sources
= dconf_engine_profile_open (filename
, &n_sources
);
109 verify_and_free (sources
, n_sources
, expected_names
, n_expected
);
111 /* next try supplying it via the environment */
112 g_setenv ("DCONF_PROFILE", filename
, TRUE
);
113 g_assert (filename_to_replace
== NULL
);
114 sources
= dconf_engine_profile_open (NULL
, &n_sources
);
115 verify_and_free (sources
, n_sources
, expected_names
, n_expected
);
116 g_unsetenv ("DCONF_PROFILE");
118 /* next try supplying a profile name via API and intercepting fopen */
119 filename_to_replace
= "/etc/dconf/profile/myprofile";
120 filename_to_replace_it_with
= filename
;
121 g_assert (g_getenv ("DCONF_PROFILE") == NULL
);
122 sources
= dconf_engine_profile_open ("myprofile", &n_sources
);
123 verify_and_free (sources
, n_sources
, expected_names
, n_expected
);
124 filename_to_replace
= NULL
;
126 /* next try the same, via the environment */
127 g_setenv ("DCONF_PROFILE", "myprofile", TRUE
);
128 filename_to_replace
= "/etc/dconf/profile/myprofile";
129 filename_to_replace_it_with
= filename
;
130 sources
= dconf_engine_profile_open (NULL
, &n_sources
);
131 verify_and_free (sources
, n_sources
, expected_names
, n_expected
);
132 g_unsetenv ("DCONF_PROFILE");
133 filename_to_replace
= NULL
;
135 /* next try to have dconf pick it up as the default user profile */
136 filename_to_replace
= "/etc/dconf/profile/user";
137 filename_to_replace_it_with
= filename
;
138 g_assert (g_getenv ("DCONF_PROFILE") == NULL
);
139 sources
= dconf_engine_profile_open (NULL
, &n_sources
);
140 verify_and_free (sources
, n_sources
, expected_names
, n_expected
);
141 filename_to_replace
= NULL
;
143 filename_to_replace_it_with
= NULL
;
144 g_free (expected_names
);
148 test_profile_parser (void)
150 DConfEngineSource
**sources
;
153 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR
))
155 g_log_set_always_fatal (G_LOG_LEVEL_ERROR
);
157 sources
= dconf_engine_profile_open (SRCDIR
"/profile/this-file-does-not-exist", &n_sources
);
158 g_assert_cmpint (n_sources
, ==, 0);
159 g_assert (sources
== NULL
);
162 g_test_trap_assert_passed ();
163 g_test_trap_assert_stderr ("*WARNING*: unable to open named profile*");
165 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR
))
167 g_log_set_always_fatal (G_LOG_LEVEL_ERROR
);
169 sources
= dconf_engine_profile_open (SRCDIR
"/profile/broken-profile", &n_sources
);
170 g_assert_cmpint (n_sources
, ==, 0);
171 g_assert (sources
== NULL
);
174 g_test_trap_assert_passed ();
175 g_test_trap_assert_stderr ("*WARNING*: unknown dconf database*unknown dconf database*");
177 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR
))
179 g_log_set_always_fatal (G_LOG_LEVEL_ERROR
);
181 sources
= dconf_engine_profile_open (SRCDIR
"/profile/gdm", &n_sources
);
182 g_assert_cmpint (n_sources
, ==, 0);
183 g_assert (sources
== NULL
);
186 g_test_trap_assert_passed ();
187 g_test_trap_assert_stderr ("*WARNING*: unknown dconf database*unknown dconf database*");
189 test_five_times (SRCDIR
"/profile/empty-profile", 0);
190 test_five_times (SRCDIR
"/profile/test-profile", 1, "test");
191 test_five_times (SRCDIR
"/profile/colourful", 4,
194 "verylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongnameverylongname",
196 test_five_times (SRCDIR
"/profile/dos", 2, "user", "site");
197 test_five_times (SRCDIR
"/profile/no-newline-longline", 0);
198 test_five_times (SRCDIR
"/profile/many-sources", 10,
199 "user", "local", "room", "floor", "building",
200 "site", "region", "division", "country", "global");
202 /* finally, test that we get the default profile if the user profile
203 * file cannot be located and we do not specify another profile.
205 filename_to_replace
= "/etc/dconf/profile/user";
206 filename_to_replace_it_with
= SRCDIR
"/profile/this-file-does-not-exist";
207 g_assert (g_getenv ("DCONF_PROFILE") == NULL
);
208 sources
= dconf_engine_profile_open (NULL
, &n_sources
);
209 filename_to_replace
= NULL
;
210 g_assert_cmpint (n_sources
, ==, 1);
211 g_assert_cmpstr (sources
[0]->name
, ==, "user");
212 dconf_engine_source_free (sources
[0]);
215 dconf_mock_shm_reset ();
219 test_signal_threadsafety_worker (gpointer user_data
)
221 gint
*finished
= user_data
;
224 for (i
= 0; i
< 20000; i
++)
228 engine
= dconf_engine_new (NULL
, NULL
, NULL
);
229 dconf_engine_unref (engine
);
232 g_atomic_int_inc (finished
);
238 test_signal_threadsafety (void)
241 GVariant
*parameters
;
245 parameters
= g_variant_new_parsed ("('/test/key', [''], 'tag')");
246 g_variant_ref_sink (parameters
);
248 for (i
= 0; i
< N_WORKERS
; i
++)
249 g_thread_unref (g_thread_new ("testcase worker", test_signal_threadsafety_worker
, &finished
));
251 while (g_atomic_int_get (&finished
) < N_WORKERS
)
252 dconf_engine_handle_dbus_signal (G_BUS_TYPE_SESSION
,
254 "/ca/desrt/dconf/Writer/user",
255 "Notify", parameters
);
256 g_variant_unref (parameters
);
258 dconf_mock_shm_reset ();
262 test_user_source (void)
264 DConfEngineSource
*source
;
269 /* Create the source from a clean slate */
270 source
= dconf_engine_source_new ("user-db:user");
271 g_assert (source
!= NULL
);
272 g_assert (source
->values
== NULL
);
273 g_assert (source
->locks
== NULL
);
275 /* Refresh it the first time.
276 * This should cause it to open the shm.
277 * FALSE should be returned because there is no database file.
279 reopened
= dconf_engine_source_refresh (source
);
280 g_assert (!reopened
);
281 dconf_mock_shm_assert_log ("open user;");
283 /* Try to refresh it. There must be no IO at this point. */
284 reopened
= dconf_engine_source_refresh (source
);
285 g_assert (!reopened
);
286 dconf_mock_shm_assert_log ("");
288 /* Add a real database. */
289 table
= dconf_mock_gvdb_table_new ();
290 dconf_mock_gvdb_table_insert (table
, "/values/int32", g_variant_new_int32 (123456), NULL
);
291 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", table
);
293 /* Try to refresh it again.
294 * Because we didn't flag the change there must still be no IO.
296 reopened
= dconf_engine_source_refresh (source
);
297 g_assert (!reopened
);
298 g_assert (source
->values
== NULL
);
299 g_assert (source
->locks
== NULL
);
300 dconf_mock_shm_assert_log ("");
302 /* Now flag it and reopen. */
303 dconf_mock_shm_flag ("user");
304 reopened
= dconf_engine_source_refresh (source
);
306 g_assert (source
->values
!= NULL
);
307 g_assert (source
->locks
== NULL
);
308 g_assert (gvdb_table_has_value (source
->values
, "/values/int32"));
309 dconf_mock_shm_assert_log ("close;open user;");
311 /* Do it again -- should get the same result, after some IO */
312 dconf_mock_shm_flag ("user");
313 reopened
= dconf_engine_source_refresh (source
);
315 g_assert (source
->values
!= NULL
);
316 g_assert (source
->locks
== NULL
);
317 dconf_mock_shm_assert_log ("close;open user;");
319 /* "Delete" the gvdb and make sure dconf notices after a flag */
320 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", NULL
);
321 dconf_mock_shm_flag ("user");
322 reopened
= dconf_engine_source_refresh (source
);
324 g_assert (source
->values
== NULL
);
325 g_assert (source
->locks
== NULL
);
326 dconf_mock_shm_assert_log ("close;open user;");
328 /* Add a gvdb with a lock */
329 table
= dconf_mock_gvdb_table_new ();
330 locks
= dconf_mock_gvdb_table_new ();
331 dconf_mock_gvdb_table_insert (table
, "/values/int32", g_variant_new_int32 (123456), NULL
);
332 dconf_mock_gvdb_table_insert (locks
, "/values/int32", g_variant_new_boolean (TRUE
), NULL
);
333 dconf_mock_gvdb_table_insert (table
, ".locks", NULL
, locks
);
334 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", table
);
336 /* Reopen and check if we have the lock */
337 dconf_mock_shm_flag ("user");
338 reopened
= dconf_engine_source_refresh (source
);
340 g_assert (source
->values
!= NULL
);
341 g_assert (source
->locks
!= NULL
);
342 g_assert (gvdb_table_has_value (source
->values
, "/values/int32"));
343 g_assert (gvdb_table_has_value (source
->locks
, "/values/int32"));
344 dconf_mock_shm_assert_log ("close;open user;");
346 /* Reopen one last time */
347 dconf_mock_shm_flag ("user");
348 reopened
= dconf_engine_source_refresh (source
);
350 g_assert (source
->values
!= NULL
);
351 g_assert (source
->locks
!= NULL
);
352 dconf_mock_shm_assert_log ("close;open user;");
354 dconf_engine_source_free (source
);
355 dconf_mock_shm_assert_log ("close;");
357 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", NULL
);
358 dconf_mock_shm_reset ();
362 test_file_source (void)
364 DConfEngineSource
*source
;
369 source
= dconf_engine_source_new ("file-db:/path/to/db");
370 g_assert (source
!= NULL
);
371 g_assert (source
->values
== NULL
);
372 g_assert (source
->locks
== NULL
);
373 g_test_expect_message ("dconf", G_LOG_LEVEL_WARNING
, "*unable to open file '/path/to/db'*");
374 reopened
= dconf_engine_source_refresh (source
);
375 g_assert (source
->values
== NULL
);
376 g_assert (source
->locks
== NULL
);
377 dconf_engine_source_free (source
);
379 source
= dconf_engine_source_new ("file-db:/path/to/db");
380 g_assert (source
!= NULL
);
381 g_assert (source
->values
== NULL
);
382 g_assert (source
->locks
== NULL
);
384 table
= dconf_mock_gvdb_table_new ();
385 dconf_mock_gvdb_table_insert (table
, "/value", g_variant_new_string ("first file"), NULL
);
386 dconf_mock_gvdb_install ("/path/to/db", table
);
388 reopened
= dconf_engine_source_refresh (source
);
390 g_assert (source
->values
);
391 g_assert (source
->locks
== NULL
);
392 value
= gvdb_table_get_value (source
->values
, "/value");
393 g_assert_cmpstr (g_variant_get_string (value
, NULL
), ==, "first file");
394 g_variant_unref (value
);
396 /* Of course this should do nothing... */
397 reopened
= dconf_engine_source_refresh (source
);
398 g_assert (!reopened
);
400 /* Invalidate and replace */
401 dconf_mock_gvdb_table_invalidate (table
);
402 table
= dconf_mock_gvdb_table_new ();
403 dconf_mock_gvdb_table_insert (table
, "/value", g_variant_new_string ("second file"), NULL
);
404 dconf_mock_gvdb_install ("/path/to/db", table
);
406 /* Even when invalidated, this should still do nothing... */
407 reopened
= dconf_engine_source_refresh (source
);
408 g_assert (!reopened
);
409 value
= gvdb_table_get_value (source
->values
, "/value");
410 g_assert_cmpstr (g_variant_get_string (value
, NULL
), ==, "first file");
411 g_variant_unref (value
);
413 dconf_mock_gvdb_install ("/path/to/db", NULL
);
414 dconf_engine_source_free (source
);
418 static gboolean service_db_created
;
419 static GvdbTable
*service_db_table
;
422 handle_service_request (GBusType bus_type
,
423 const gchar
*bus_name
,
424 const gchar
*object_path
,
425 const gchar
*interface_name
,
426 const gchar
*method_name
,
427 GVariant
*parameters
,
428 const GVariantType
*expected_type
,
431 g_assert_cmpstr (bus_name
, ==, "ca.desrt.dconf");
432 g_assert_cmpstr (interface_name
, ==, "ca.desrt.dconf.Writer");
433 g_assert_cmpstr (method_name
, ==, "Init");
434 g_assert_cmpstr (g_variant_get_type_string (parameters
), ==, "()");
436 if (g_str_equal (object_path
, "/ca/desrt/dconf/shm/nil"))
438 service_db_table
= dconf_mock_gvdb_table_new ();
439 dconf_mock_gvdb_table_insert (service_db_table
, "/values/int32", g_variant_new_int32 (123456), NULL
);
440 dconf_mock_gvdb_install ("/RUNTIME/dconf-service/shm/nil", service_db_table
);
442 /* Make sure this only happens the first time... */
443 g_assert (!service_db_created
);
444 service_db_created
= TRUE
;
446 return g_variant_new ("()");
450 g_set_error_literal (error
, G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "Unknown DB type");
456 test_service_source (void)
458 DConfEngineSource
*source
;
461 /* Make sure we deal with errors from the service sensibly */
462 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR
))
464 g_log_set_always_fatal (G_LOG_LEVEL_ERROR
);
466 source
= dconf_engine_source_new ("service-db:unknown/nil");
467 dconf_mock_dbus_sync_call_handler
= handle_service_request
;
468 g_assert (source
!= NULL
);
469 g_assert (source
->values
== NULL
);
470 g_assert (source
->locks
== NULL
);
471 reopened
= dconf_engine_source_refresh (source
);
475 g_test_trap_assert_passed ();
476 g_test_trap_assert_stderr ("*WARNING*: unable to open file*unknown/nil*expect degraded performance*");
478 /* Set up one that will work */
479 source
= dconf_engine_source_new ("service-db:shm/nil");
480 g_assert (source
!= NULL
);
481 g_assert (source
->values
== NULL
);
482 g_assert (source
->locks
== NULL
);
484 /* Refresh it the first time.
486 * This should cause the service to be asked to create it.
488 * This should return TRUE because we just opened it.
490 dconf_mock_dbus_sync_call_handler
= handle_service_request
;
491 reopened
= dconf_engine_source_refresh (source
);
492 dconf_mock_dbus_sync_call_handler
= NULL
;
493 g_assert (service_db_created
);
496 /* After that, a refresh should be a no-op. */
497 reopened
= dconf_engine_source_refresh (source
);
498 g_assert (!reopened
);
500 /* Close it and reopen it, ensuring that we don't hit the service
501 * again (because the file already exists).
503 * Note: dconf_mock_dbus_sync_call_handler = NULL, so D-Bus calls will
506 dconf_engine_source_free (source
);
507 source
= dconf_engine_source_new ("service-db:shm/nil");
508 g_assert (source
!= NULL
);
509 reopened
= dconf_engine_source_refresh (source
);
512 /* Make sure it has the content we expect to see */
513 g_assert (gvdb_table_has_value (source
->values
, "/values/int32"));
515 /* Now invalidate it and replace it with an empty one */
516 dconf_mock_gvdb_table_invalidate (service_db_table
);
517 service_db_table
= dconf_mock_gvdb_table_new ();
518 dconf_mock_gvdb_install ("/RUNTIME/dconf-service/shm/nil", service_db_table
);
520 /* Now reopening should get the new one */
521 reopened
= dconf_engine_source_refresh (source
);
524 /* ...and we should find it to be empty */
525 g_assert (!gvdb_table_has_value (source
->values
, "/values/int32"));
528 dconf_engine_source_free (source
);
530 /* This should not have done any shm... */
531 dconf_mock_shm_assert_log ("");
533 dconf_mock_gvdb_install ("/RUNTIME/dconf-service/shm/nil", NULL
);
534 service_db_table
= NULL
;
538 test_system_source (void)
540 DConfEngineSource
*source
;
541 GvdbTable
*first_table
;
542 GvdbTable
*next_table
;
545 source
= dconf_engine_source_new ("system-db:site");
546 g_assert (source
!= NULL
);
548 /* Check to see that we get the warning about the missing file. */
549 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR
))
551 g_log_set_always_fatal (G_LOG_LEVEL_ERROR
);
553 /* Failing to open should return FALSE from refresh */
554 reopened
= dconf_engine_source_refresh (source
);
555 g_assert (!reopened
);
556 g_assert (source
->values
== NULL
);
558 /* Attempt the reopen to make sure we don't get two warnings.
559 * We should see FALSE again since we go from NULL to NULL.
561 reopened
= dconf_engine_source_refresh (source
);
562 g_assert (!reopened
);
564 /* Create the file after the fact and make sure it opens properly */
565 first_table
= dconf_mock_gvdb_table_new ();
566 dconf_mock_gvdb_install ("/etc/dconf/db/site", first_table
);
568 reopened
= dconf_engine_source_refresh (source
);
570 g_assert (source
->values
!= NULL
);
572 dconf_engine_source_free (source
);
576 g_test_trap_assert_passed ();
577 /* Check that we only saw the warning, but only one time. */
578 g_test_trap_assert_stderr ("*this gvdb does not exist; expect degraded performance*");
579 g_test_trap_assert_stderr_unmatched ("*degraded*degraded*");
581 /* Create the file before the first refresh attempt */
582 first_table
= dconf_mock_gvdb_table_new ();
583 dconf_mock_gvdb_install ("/etc/dconf/db/site", first_table
);
584 /* Hang on to a copy for ourselves for below... */
585 dconf_mock_gvdb_table_ref (first_table
);
587 /* See that we get the database. */
588 reopened
= dconf_engine_source_refresh (source
);
590 g_assert (source
->values
== first_table
);
592 /* Do a refresh, make sure there is no change. */
593 reopened
= dconf_engine_source_refresh (source
);
594 g_assert (!reopened
);
595 g_assert (source
->values
== first_table
);
597 /* Replace the table on "disk" but don't invalidate the old one */
598 next_table
= dconf_mock_gvdb_table_new ();
599 dconf_mock_gvdb_install ("/etc/dconf/db/site", next_table
);
601 /* Make sure the old table remains open (ie: no IO performed) */
602 reopened
= dconf_engine_source_refresh (source
);
603 g_assert (!reopened
);
604 g_assert (source
->values
== first_table
);
606 /* Now mark the first table invalid and reopen */
607 dconf_mock_gvdb_table_invalidate (first_table
);
608 gvdb_table_free (first_table
);
609 reopened
= dconf_engine_source_refresh (source
);
611 g_assert (source
->values
== next_table
);
613 /* Remove the file entirely and do the same thing */
614 dconf_mock_gvdb_install ("/etc/dconf/db/site", NULL
);
615 reopened
= dconf_engine_source_refresh (source
);
616 g_assert (!reopened
);
618 dconf_engine_source_free (source
);
622 invalidate_state (guint n_sources
,
628 for (i
= 0; i
< n_sources
; i
++)
629 if (source_types
& (1u << i
))
633 dconf_mock_gvdb_table_invalidate (state
[i
]);
634 gvdb_table_free (state
[i
]);
639 dconf_mock_shm_flag (state
[i
]);
645 setup_state (guint n_sources
,
647 guint database_state
,
652 for (i
= 0; i
< n_sources
; i
++)
654 guint contents
= database_state
% 7;
655 GvdbTable
*table
= NULL
;
660 table
= dconf_mock_gvdb_table_new ();
662 /* Even numbers get the value setup */
663 if ((contents
& 1) == 0)
664 dconf_mock_gvdb_table_insert (table
, "/value", g_variant_new_uint32 (i
), NULL
);
666 /* Numbers above 2 get the locks table */
671 locks
= dconf_mock_gvdb_table_new ();
673 /* Numbers above 4 get the lock set */
675 dconf_mock_gvdb_table_insert (locks
, "/value", g_variant_new_boolean (TRUE
), NULL
);
677 dconf_mock_gvdb_table_insert (table
, ".locks", NULL
, locks
);
681 if (source_types
& (1u << i
))
686 state
[i
] = dconf_mock_gvdb_table_ref (table
);
691 filename
= g_strdup_printf ("/etc/dconf/db/db%d", i
);
696 state
[i
] = g_strdup_printf ("db%d", i
);
698 filename
= g_strdup_printf ("/HOME/.config/dconf/db%d", i
);
701 dconf_mock_gvdb_install (filename
, table
);
709 create_profile (const gchar
*filename
,
713 GError
*error
= NULL
;
717 profile
= g_string_new (NULL
);
718 for (i
= 0; i
< n_sources
; i
++)
719 if (source_types
& (1u << i
))
720 g_string_append_printf (profile
, "system-db:db%d\n", i
);
722 g_string_append_printf (profile
, "user-db:db%d\n", i
);
723 g_file_set_contents (filename
, profile
->str
, profile
->len
, &error
);
724 g_assert_no_error (error
);
725 g_string_free (profile
, TRUE
);
728 static GQueue read_through_queues
[12];
731 check_read (DConfEngine
*engine
,
734 guint database_state
)
736 gboolean any_values
= FALSE
;
737 gboolean any_locks
= FALSE
;
738 guint first_contents
;
739 gint underlying
= -1;
747 /* The value we expect to read is number of the first source that has
748 * the value set (ie: odd digit in database_state) up to the lowest
751 * We go over each database. If 'expected' has not yet been set and
752 * we find that we should have a value in this database, we set it.
753 * If we find that we should have a lock in this database, we unset
754 * any previous values (since they should not have been written).
756 * We intentionally code this loop in a different way than the one in
757 * dconf itself is currently implemented...
759 * We also take note of if we saw any locks and cross-check that with
760 * dconf_engine_is_writable(). We check if we saw and values at all
761 * and cross-check that with dconf_engine_list() (which ignores
764 first_contents
= database_state
% 7;
765 for (i
= 0; i
< n_sources
; i
++)
767 guint contents
= database_state
% 7;
769 /* A lock here should prevent higher reads */
772 /* Locks in the first database don't count... */
778 /* A value here should be read */
779 if (contents
&& !(contents
& 1))
781 if (i
!= 0 && underlying
== -1)
794 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "/value");
798 g_assert (g_variant_is_of_type (value
, G_VARIANT_TYPE_UINT32
));
799 g_assert_cmpint (g_variant_get_uint32 (value
), ==, expected
);
800 g_variant_unref (value
);
803 g_assert (value
== NULL
);
805 /* We are writable if the first database is a user database and we
806 * didn't encounter any locks...
808 writable
= dconf_engine_is_writable (engine
, "/value");
809 g_assert_cmpint (writable
, ==, n_sources
&& !(source_types
& 1) && !any_locks
);
811 /* Check various read-through scenarios. Read-through should only be
812 * effective if the database is writable.
814 for (i
= 0; i
< G_N_ELEMENTS (read_through_queues
); i
++)
816 gint our_expected
= expected
;
820 /* If writable, see what our changeset did.
823 * 1: reset value (should see underlying value)
824 * 2: set value to 123
827 our_expected
= underlying
;
828 else if ((i
% 3) == 2)
832 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, &read_through_queues
[i
], "/value");
834 if (our_expected
!= -1)
836 g_assert (g_variant_is_of_type (value
, G_VARIANT_TYPE_UINT32
));
837 g_assert_cmpint (g_variant_get_uint32 (value
), ==, our_expected
);
838 g_variant_unref (value
);
841 g_assert (value
== NULL
);
845 g_strfreev (dconf_engine_list (engine
, "/", &n
));
846 list
= dconf_engine_list (engine
, "/", NULL
);
847 g_assert_cmpint (g_strv_length (list
), ==, n
);
850 g_assert_cmpstr (list
[0], ==, "value");
851 g_assert (list
[1] == NULL
);
854 g_assert (list
[0] == NULL
);
857 /* Check the user value.
859 * This should be set only in the case that the first database is a
860 * user database (ie: writable) and the contents of that database are
861 * set (ie: 2, 4 or 6). See the table in the comment below.
863 * Note: we do not consider locks.
865 value
= dconf_engine_read (engine
, DCONF_READ_USER_VALUE
, NULL
, "/value");
868 g_assert (first_contents
&& !(first_contents
& 1) && !(source_types
& 1));
869 g_assert (g_variant_is_of_type (value
, G_VARIANT_TYPE_UINT32
));
870 g_assert_cmpint (g_variant_get_uint32 (value
), ==, 0);
871 g_variant_unref (value
);
875 /* Three possibilities for failure:
876 * - first db did not exist
877 * - value was missing from first db
878 * - first DB was system-db
880 g_assert (!first_contents
|| (first_contents
& 1) || (source_types
& 1));
883 /* Check read_through vs. user-value */
884 for (i
= 0; i
< G_N_ELEMENTS (read_through_queues
); i
++)
886 /* It is only possible here to see one of three possibilities:
889 * - 0 (value from user's DB)
890 * - 123 (value from queue)
892 * We see these values regardless of writability. We do however
893 * ensure that we have a writable database as the first one.
895 value
= dconf_engine_read (engine
, DCONF_READ_USER_VALUE
, &read_through_queues
[i
], "/value");
897 /* If we have no first source, or the first source is non-user
898 * than we should always do nothing (since we can't queue changes
899 * against a system db or one that doesn't exist).
901 if (n_sources
== 0 || (source_types
& 1) || (i
% 3) == 0)
903 /* Changeset did nothing, so it should be same as above. */
906 g_assert (first_contents
&& !(first_contents
& 1) && !(source_types
& 1));
907 g_assert (g_variant_is_of_type (value
, G_VARIANT_TYPE_UINT32
));
908 g_assert_cmpint (g_variant_get_uint32 (value
), ==, 0);
911 g_assert (!first_contents
|| (first_contents
& 1) || (source_types
& 1));
913 else if ((i
% 3) == 1)
915 /* Changeset did a reset, so we should always see NULL */
916 g_assert (value
== NULL
);
918 else if ((i
% 3) == 2)
920 /* Changeset set a value, so we should see it */
921 g_assert_cmpint (g_variant_get_uint32 (value
), ==, 123);
925 g_variant_unref (value
);
930 is_expected (const gchar
*log_domain
,
931 GLogLevelFlags log_level
,
932 const gchar
*message
)
934 return g_str_equal (log_domain
, "dconf") &&
935 log_level
== (G_LOG_LEVEL_WARNING
| G_LOG_FLAG_FATAL
) &&
936 strstr (message
, "unable to open file '/etc/dconf/db");
940 fatal_handler (const gchar
*log_domain
,
941 GLogLevelFlags log_level
,
942 const gchar
*message
,
945 return !is_expected (log_domain
, log_level
, message
);
949 normal_handler (const gchar
*log_domain
,
950 GLogLevelFlags log_level
,
951 const gchar
*message
,
954 if (!is_expected (log_domain
, log_level
, message
))
955 g_error ("unexpected error: %s\n", message
);
961 #define MAX_N_SOURCES 2
962 gpointer state
[MAX_N_SOURCES
];
963 gchar
*profile_filename
;
964 GError
*error
= NULL
;
970 /* This test throws a lot of messages about missing databases.
971 * Capture and ignore them.
973 g_test_log_set_fatal_handler (fatal_handler
, NULL
);
974 handler_id
= g_log_set_handler ("dconf", G_LOG_LEVEL_WARNING
| G_LOG_FLAG_FATAL
, normal_handler
, NULL
);
976 /* Our test strategy is as follows:
978 * We only test a single key name. It is assumed that gvdb is working
979 * properly already so we are only interested in interactions between
980 * multiple databases for a given key name.
982 * The outermost loop is over 'n'. This is how many sources are in
983 * our test. We test 0 to 3 (which should be enough to cover all
984 * 'interesting' possibilities). 4 takes too long to run (2*7*7 ~=
985 * 100 times as long as 3).
987 * The next loop is over 'i'. This goes from 0 to 2^n - 1, with each
988 * bit deciding the type of source of the i-th element
994 * The next loop is over 'j'. This goes from 0 to 7^n - 1, with each
995 * base-7 digit deciding the state of the database file associated
996 * with the i-th source:
998 * j file has value has ".locks" has lock
999 * ----------------------------------------------------
1008 * Where 'file' is if the database file exists, 'has value' is if a
1009 * value exists at '/value' within the file, 'has ".locks"' is if
1010 * there is a ".locks" subtable and 'has lock' is if there is a lock
1011 * for '/value' within that table.
1013 * Finally, we loop over 'k' as a state to transition to ('k' works
1014 * the same way as 'j').
1016 * Once we know 'n' and 'i', we can write a profile file.
1018 * Once we know 'j' we can setup the initial state, create the engine
1019 * and check that we got the expected value. Then we transition to
1020 * state 'k' and make sure everything still works as expected.
1022 * Since we want to test all j->k transitions, we do the initial setup
1023 * of the engine (according to j) inside of the 'k' loop, since we
1024 * need to test all possible transitions from 'j'.
1026 * We additionally test the effect of read-through queues in 4
1030 * - 0: queue with no effect
1031 * - 1: queue that resets the value
1032 * - 2: queue that sets the value to 123
1034 * For the cases (0, 1, 2) we can have multiple types of queue that
1035 * achieve the desired effect. We can put more than 3 items in
1036 * read_through_queues -- the expected behaviour is dictated by the
1037 * value of (i % 3) where i is the array index.
1040 /* We use a scheme to set up each queue. Again, we assume that
1041 * GHashTable is working OK, so we only bother having "/value" as a
1042 * changeset item (or not).
1044 * We have an array of strings, each string defining the
1045 * configuration of one queue. In each string, each character
1046 * represents the contents of a changeset within the queue, in
1049 * ' ' - empty changeset
1050 * 's' - set value to 123
1052 * 'x' - set value to 321
1054 const gchar
*queue_configs
[] = {
1062 G_STATIC_ASSERT (G_N_ELEMENTS (queue_configs
) == G_N_ELEMENTS (read_through_queues
));
1063 for (i
= 0; i
< G_N_ELEMENTS (read_through_queues
); i
++)
1065 const gchar
*conf
= queue_configs
[i
];
1068 for (j
= 0; conf
[j
]; j
++)
1070 DConfChangeset
*changeset
;
1072 changeset
= dconf_changeset_new ();
1079 dconf_changeset_set (changeset
, "/value", NULL
);
1082 dconf_changeset_set (changeset
, "/value", g_variant_new_uint32 (123));
1085 dconf_changeset_set (changeset
, "/value", g_variant_new_uint32 (321));
1088 g_assert_not_reached ();
1091 g_queue_push_head (&read_through_queues
[i
], changeset
);
1096 /* We need a place to put the profile files we use for this test */
1097 close (g_file_open_tmp ("dconf-testcase.XXXXXX", &profile_filename
, &error
));
1098 g_assert_no_error (error
);
1100 for (n
= 0; n
<= MAX_N_SOURCES
; n
++)
1101 for (i
= 0; i
< pow (2, n
); i
++)
1103 gint n_possible_states
= pow (7, n
);
1105 /* Step 1: write out the profile file */
1106 create_profile (profile_filename
, n
, i
);
1108 for (j
= 0; j
< n_possible_states
; j
++)
1109 for (k
= 0; k
< n_possible_states
; k
++)
1111 guint64 old_state
, new_state
;
1113 /* Step 2: setup the state */
1114 setup_state (n
, i
, j
, (j
!= k
) ? state
: NULL
);
1116 /* Step 3: create the engine */
1117 engine
= dconf_engine_new (profile_filename
, NULL
, NULL
);
1119 /* Step 4: read, and check result */
1120 check_read (engine
, n
, i
, j
);
1121 old_state
= dconf_engine_get_state (engine
);
1123 /* Step 5: change to the new state */
1126 setup_state (n
, i
, k
, NULL
);
1127 invalidate_state (n
, i
, state
);
1130 /* Step 6: read, and check result */
1131 check_read (engine
, n
, i
, k
);
1132 new_state
= dconf_engine_get_state (engine
);
1134 g_assert ((j
== k
) == (new_state
== old_state
));
1137 setup_state (n
, i
, 0, NULL
);
1138 dconf_engine_unref (engine
);
1142 /* Clean up the tempfile we were using... */
1143 g_unlink (profile_filename
);
1144 g_free (profile_filename
);
1145 dconf_mock_shm_reset ();
1147 g_log_remove_handler ("dconf", handler_id
);
1151 test_watch_fast (void)
1153 DConfEngine
*engine
;
1158 change_log
= g_string_new (NULL
);
1160 table
= dconf_mock_gvdb_table_new ();
1161 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", table
);
1162 table
= dconf_mock_gvdb_table_new ();
1163 dconf_mock_gvdb_install ("/etc/dconf/db/site", table
);
1165 triv
= g_variant_ref_sink (g_variant_new ("()"));
1167 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1169 /* Check that establishing a watch works properly in the normal case.
1171 a
= dconf_engine_get_state (engine
);
1172 dconf_engine_watch_fast (engine
, "/a/b/c");
1173 /* watches do not count as outstanding changes */
1174 g_assert (!dconf_engine_has_outstanding (engine
));
1175 dconf_engine_sync (engine
);
1176 b
= dconf_engine_get_state (engine
);
1177 g_assert_cmpuint (a
, ==, b
);
1178 /* both AddMatch results come back before shm is flagged */
1179 dconf_mock_dbus_async_reply (triv
, NULL
);
1180 dconf_mock_dbus_async_reply (triv
, NULL
);
1181 dconf_mock_dbus_assert_no_async ();
1182 dconf_mock_shm_flag ("user");
1183 b
= dconf_engine_get_state (engine
);
1184 g_assert_cmpuint (a
, !=, b
);
1185 g_assert_cmpstr (change_log
->str
, ==, "");
1186 dconf_engine_unwatch_fast (engine
, "/a/b/c");
1187 dconf_mock_dbus_async_reply (triv
, NULL
);
1188 dconf_mock_dbus_async_reply (triv
, NULL
);
1189 dconf_mock_dbus_assert_no_async ();
1191 /* Establish a watch and fail the race. */
1192 a
= dconf_engine_get_state (engine
);
1193 dconf_engine_watch_fast (engine
, "/a/b/c");
1194 g_assert (!dconf_engine_has_outstanding (engine
));
1195 dconf_engine_sync (engine
);
1196 b
= dconf_engine_get_state (engine
);
1197 g_assert_cmpuint (a
, ==, b
);
1198 /* one AddMatch result comes back -after- shm is flagged */
1199 dconf_mock_dbus_async_reply (triv
, NULL
);
1200 dconf_mock_shm_flag ("user");
1201 dconf_mock_dbus_async_reply (triv
, NULL
);
1202 dconf_mock_dbus_assert_no_async ();
1203 b
= dconf_engine_get_state (engine
);
1204 g_assert_cmpuint (a
, !=, b
);
1205 g_assert_cmpstr (change_log
->str
, ==, "/a/b/c:1::nil;");
1206 /* Try to establish a watch again for the same path */
1207 dconf_engine_watch_fast (engine
, "/a/b/c");
1208 g_assert (!dconf_engine_has_outstanding (engine
));
1209 dconf_engine_sync (engine
);
1210 c
= dconf_engine_get_state (engine
);
1211 g_assert_cmpuint (b
, ==, c
);
1212 /* The watch result was not sent, because the path was already watched */
1213 dconf_mock_dbus_assert_no_async();
1214 c
= dconf_engine_get_state (engine
);
1215 g_assert_cmpuint (b
, ==, c
);
1216 /* Since the path was already being watched,
1217 * do not expect a second false change notification */
1218 g_assert_cmpstr (change_log
->str
, ==, "/a/b/c:1::nil;");
1219 dconf_engine_unwatch_fast (engine
, "/a/b/c");
1220 dconf_mock_dbus_async_reply (triv
, NULL
);
1221 dconf_mock_dbus_async_reply (triv
, NULL
);
1222 dconf_mock_dbus_assert_no_async ();
1224 dconf_mock_gvdb_install ("/HOME/.config/dconf/user", NULL
);
1225 dconf_mock_gvdb_install ("/etc/dconf/db/site", NULL
);
1226 dconf_engine_unref (engine
);
1227 g_string_free (change_log
, TRUE
);
1229 g_variant_unref (triv
);
1232 static const gchar
*match_request_type
;
1233 static gboolean got_match_request
[5];
1236 handle_match_request (GBusType bus_type
,
1237 const gchar
*bus_name
,
1238 const gchar
*object_path
,
1239 const gchar
*interface_name
,
1240 const gchar
*method_name
,
1241 GVariant
*parameters
,
1242 const GVariantType
*expected_type
,
1245 const gchar
*match_rule
;
1247 g_assert_cmpstr (bus_name
, ==, "org.freedesktop.DBus");
1248 /* any object path works... */
1249 g_assert_cmpstr (interface_name
, ==, "org.freedesktop.DBus");
1250 g_assert_cmpstr (method_name
, ==, match_request_type
);
1251 g_assert_cmpstr (g_variant_get_type_string (parameters
), ==, "(s)");
1252 g_variant_get (parameters
, "(&s)", &match_rule
);
1253 g_assert (strstr (match_rule
, "arg0path='/a/b/c'"));
1254 g_assert (!got_match_request
[bus_type
]);
1255 got_match_request
[bus_type
] = TRUE
;
1257 return g_variant_new ("()");
1261 test_watch_sync (void)
1263 DConfEngine
*engine
;
1265 dconf_mock_dbus_sync_call_handler
= handle_match_request
;
1267 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1269 match_request_type
= "AddMatch";
1270 dconf_engine_watch_sync (engine
, "/a/b/c");
1271 g_assert (got_match_request
[G_BUS_TYPE_SESSION
]);
1272 g_assert (got_match_request
[G_BUS_TYPE_SYSTEM
]);
1273 got_match_request
[G_BUS_TYPE_SESSION
] = FALSE
;
1274 got_match_request
[G_BUS_TYPE_SYSTEM
] = FALSE
;
1276 match_request_type
= "RemoveMatch";
1277 dconf_engine_unwatch_sync (engine
, "/a/b/c");
1278 g_assert (got_match_request
[G_BUS_TYPE_SESSION
]);
1279 g_assert (got_match_request
[G_BUS_TYPE_SYSTEM
]);
1280 got_match_request
[G_BUS_TYPE_SESSION
] = FALSE
;
1281 got_match_request
[G_BUS_TYPE_SYSTEM
] = FALSE
;
1283 dconf_engine_unref (engine
);
1285 dconf_mock_dbus_sync_call_handler
= NULL
;
1286 match_request_type
= NULL
;
1290 test_watching (void)
1292 DConfEngine
*engine
;
1293 const gchar
*apple
= "apple";
1294 const gchar
*orange
= "orange";
1295 const gchar
*banana
= "banana";
1297 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1299 g_assert (!dconf_engine_is_watching(engine
, apple
, TRUE
));
1300 g_assert (!dconf_engine_is_watching(engine
, apple
, FALSE
));
1301 g_assert (!dconf_engine_is_watching(engine
, orange
, TRUE
));
1302 g_assert (!dconf_engine_is_watching(engine
, orange
, FALSE
));
1303 g_assert (!dconf_engine_is_watching(engine
, banana
, TRUE
));
1304 g_assert (!dconf_engine_is_watching(engine
, banana
, FALSE
));
1306 dconf_engine_set_watching (engine
, apple
, FALSE
, FALSE
);
1307 dconf_engine_set_watching (engine
, orange
, TRUE
, FALSE
);
1308 dconf_engine_set_watching (engine
, banana
, TRUE
, TRUE
);
1310 g_assert (!dconf_engine_is_watching(engine
, apple
, TRUE
));
1311 g_assert (!dconf_engine_is_watching(engine
, apple
, FALSE
));
1312 g_assert (!dconf_engine_is_watching(engine
, orange
, TRUE
));
1313 g_assert (dconf_engine_is_watching(engine
, orange
, FALSE
));
1314 g_assert (dconf_engine_is_watching(engine
, banana
, TRUE
));
1315 g_assert (dconf_engine_is_watching(engine
, banana
, FALSE
));
1317 dconf_engine_set_watching (engine
, orange
, TRUE
, TRUE
);
1318 dconf_engine_set_watching (engine
, banana
, FALSE
, FALSE
);
1320 g_assert (!dconf_engine_is_watching(engine
, apple
, TRUE
));
1321 g_assert (!dconf_engine_is_watching(engine
, apple
, FALSE
));
1322 g_assert (dconf_engine_is_watching(engine
, orange
, TRUE
));
1323 g_assert (dconf_engine_is_watching(engine
, orange
, FALSE
));
1324 g_assert (!dconf_engine_is_watching(engine
, banana
, TRUE
));
1325 g_assert (!dconf_engine_is_watching(engine
, banana
, FALSE
));
1327 dconf_engine_set_watching (engine
, orange
, FALSE
, FALSE
);
1329 g_assert (!dconf_engine_is_watching(engine
, apple
, TRUE
));
1330 g_assert (!dconf_engine_is_watching(engine
, apple
, FALSE
));
1331 g_assert (!dconf_engine_is_watching(engine
, orange
, TRUE
));
1332 g_assert (!dconf_engine_is_watching(engine
, orange
, FALSE
));
1333 g_assert (!dconf_engine_is_watching(engine
, banana
, TRUE
));
1334 g_assert (!dconf_engine_is_watching(engine
, banana
, FALSE
));
1338 test_change_fast (void)
1340 DConfChangeset
*empty
, *good_write
, *bad_write
, *very_good_write
, *slightly_bad_write
;
1341 GvdbTable
*table
, *locks
;
1342 DConfEngine
*engine
;
1344 GError
*error
= NULL
;
1347 change_log
= g_string_new (NULL
);
1349 table
= dconf_mock_gvdb_table_new ();
1350 locks
= dconf_mock_gvdb_table_new ();
1351 dconf_mock_gvdb_table_insert (locks
, "/locked", g_variant_new_boolean (TRUE
), NULL
);
1352 dconf_mock_gvdb_table_insert (table
, ".locks", NULL
, locks
);
1353 dconf_mock_gvdb_install ("/etc/dconf/db/site", table
);
1355 empty
= dconf_changeset_new ();
1356 good_write
= dconf_changeset_new_write ("/value", g_variant_new_string ("value"));
1357 bad_write
= dconf_changeset_new_write ("/locked", g_variant_new_string ("value"));
1358 very_good_write
= dconf_changeset_new_write ("/value", g_variant_new_string ("value"));
1359 dconf_changeset_set (very_good_write
, "/to-reset", NULL
);
1360 slightly_bad_write
= dconf_changeset_new_write ("/locked", g_variant_new_string ("value"));
1361 dconf_changeset_set (slightly_bad_write
, "/to-reset", NULL
);
1363 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1365 success
= dconf_engine_change_fast (engine
, empty
, NULL
, &error
);
1366 g_assert_no_error (error
);
1369 success
= dconf_engine_change_fast (engine
, empty
, NULL
, &error
);
1370 g_assert_no_error (error
);
1373 success
= dconf_engine_change_fast (engine
, bad_write
, NULL
, &error
);
1374 g_assert_error (error
, DCONF_ERROR
, DCONF_ERROR_NOT_WRITABLE
);
1375 g_clear_error (&error
);
1376 g_assert (!success
);
1378 success
= dconf_engine_change_fast (engine
, slightly_bad_write
, NULL
, &error
);
1379 g_assert_error (error
, DCONF_ERROR
, DCONF_ERROR_NOT_WRITABLE
);
1380 g_clear_error (&error
);
1381 g_assert (!success
);
1383 /* Up to now, no D-Bus traffic should have been sent at all because we
1384 * only had trivial and non-writable attempts.
1386 * Now try some working cases
1388 dconf_mock_dbus_assert_no_async ();
1389 g_assert_cmpstr (change_log
->str
, ==, "");
1391 success
= dconf_engine_change_fast (engine
, good_write
, NULL
, &error
);
1392 g_assert_no_error (error
);
1395 /* That should have emitted a synthetic change event */
1396 g_assert_cmpstr (change_log
->str
, ==, "/value:1::nil;");
1397 g_string_set_size (change_log
, 0);
1399 /* Verify that the value is set */
1400 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "/value");
1401 g_assert_cmpstr (g_variant_get_string (value
, NULL
), ==, "value");
1402 g_variant_unref (value
);
1404 /* Fail the attempted write. This should cause a warning and a change. */
1405 g_test_expect_message ("dconf", G_LOG_LEVEL_WARNING
, "failed to commit changes to dconf: something failed");
1406 error
= g_error_new_literal (G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "something failed");
1407 dconf_mock_dbus_async_reply (NULL
, error
);
1408 g_clear_error (&error
);
1409 g_assert_cmpstr (change_log
->str
, ==, "/value:1::nil;");
1410 g_string_set_size (change_log
, 0);
1412 /* Verify that the value became unset due to the failure */
1413 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "value");
1414 g_assert (value
== NULL
);
1416 /* Now try a successful write */
1417 dconf_mock_dbus_assert_no_async ();
1418 g_assert_cmpstr (change_log
->str
, ==, "");
1420 success
= dconf_engine_change_fast (engine
, good_write
, NULL
, &error
);
1421 g_assert_no_error (error
);
1424 /* That should have emitted a synthetic change event */
1425 g_assert_cmpstr (change_log
->str
, ==, "/value:1::nil;");
1426 g_string_set_size (change_log
, 0);
1428 /* Verify that the value is set */
1429 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "/value");
1430 g_assert_cmpstr (g_variant_get_string (value
, NULL
), ==, "value");
1431 g_variant_unref (value
);
1433 /* ACK the write. */
1434 error
= g_error_new_literal (G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "something failed");
1435 dconf_mock_dbus_async_reply (g_variant_new ("(s)", "tag"), NULL
);
1436 g_clear_error (&error
);
1437 /* No change this time, since we already did it. */
1438 g_assert_cmpstr (change_log
->str
, ==, "");
1440 /* Verify that the value became unset due to the in-flight queue
1442 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "value");
1443 g_assert (value
== NULL
);
1445 /* Do that all again for a changeset with more than one item */
1446 dconf_mock_dbus_assert_no_async ();
1447 g_assert_cmpstr (change_log
->str
, ==, "");
1448 success
= dconf_engine_change_fast (engine
, very_good_write
, NULL
, &error
);
1449 g_assert_no_error (error
);
1451 g_assert_cmpstr (change_log
->str
, ==, "/:2:to-reset,value:nil;");
1452 g_string_set_size (change_log
, 0);
1453 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "/value");
1454 g_assert_cmpstr (g_variant_get_string (value
, NULL
), ==, "value");
1455 g_variant_unref (value
);
1456 g_test_expect_message ("dconf", G_LOG_LEVEL_WARNING
, "failed to commit changes to dconf: something failed");
1457 error
= g_error_new_literal (G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "something failed");
1458 dconf_mock_dbus_async_reply (NULL
, error
);
1459 g_clear_error (&error
);
1460 g_assert_cmpstr (change_log
->str
, ==, "/:2:to-reset,value:nil;");
1461 g_string_set_size (change_log
, 0);
1462 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "value");
1463 g_assert (value
== NULL
);
1464 dconf_mock_dbus_assert_no_async ();
1465 g_assert_cmpstr (change_log
->str
, ==, "");
1466 success
= dconf_engine_change_fast (engine
, very_good_write
, NULL
, &error
);
1467 g_assert_no_error (error
);
1469 g_assert_cmpstr (change_log
->str
, ==, "/:2:to-reset,value:nil;");
1470 g_string_set_size (change_log
, 0);
1471 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "/value");
1472 g_assert_cmpstr (g_variant_get_string (value
, NULL
), ==, "value");
1473 g_variant_unref (value
);
1474 error
= g_error_new_literal (G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "something failed");
1475 dconf_mock_dbus_async_reply (g_variant_new ("(s)", "tag"), NULL
);
1476 g_clear_error (&error
);
1477 g_assert_cmpstr (change_log
->str
, ==, "");
1478 value
= dconf_engine_read (engine
, DCONF_READ_FLAGS_NONE
, NULL
, "value");
1479 g_assert (value
== NULL
);
1481 dconf_engine_unref (engine
);
1483 dconf_changeset_unref (empty
);
1484 dconf_changeset_unref (good_write
);
1485 dconf_changeset_unref (very_good_write
);
1486 dconf_changeset_unref (bad_write
);
1487 dconf_changeset_unref (slightly_bad_write
);
1488 g_string_free (change_log
, TRUE
);
1492 static GError
*change_sync_error
;
1493 static GVariant
*change_sync_result
;
1496 handle_write_request (GBusType bus_type
,
1497 const gchar
*bus_name
,
1498 const gchar
*object_path
,
1499 const gchar
*interface_name
,
1500 const gchar
*method_name
,
1501 GVariant
*parameters
,
1502 const GVariantType
*expected_type
,
1505 g_assert_cmpstr (bus_name
, ==, "ca.desrt.dconf");
1506 g_assert_cmpstr (interface_name
, ==, "ca.desrt.dconf.Writer");
1508 /* Assume that the engine can format the method call properly, but
1509 * test that it can properly handle weird replies.
1512 *error
= change_sync_error
;
1513 return change_sync_result
;
1518 test_change_sync (void)
1520 DConfChangeset
*empty
, *good_write
, *bad_write
, *very_good_write
, *slightly_bad_write
;
1521 GvdbTable
*table
, *locks
;
1522 DConfEngine
*engine
;
1524 GError
*error
= NULL
;
1527 table
= dconf_mock_gvdb_table_new ();
1528 locks
= dconf_mock_gvdb_table_new ();
1529 dconf_mock_gvdb_table_insert (locks
, "/locked", g_variant_new_boolean (TRUE
), NULL
);
1530 dconf_mock_gvdb_table_insert (table
, ".locks", NULL
, locks
);
1531 dconf_mock_gvdb_install ("/etc/dconf/db/site", table
);
1533 empty
= dconf_changeset_new ();
1534 good_write
= dconf_changeset_new_write ("/value", g_variant_new_string ("value"));
1535 bad_write
= dconf_changeset_new_write ("/locked", g_variant_new_string ("value"));
1536 very_good_write
= dconf_changeset_new_write ("/value", g_variant_new_string ("value"));
1537 dconf_changeset_set (very_good_write
, "/to-reset", NULL
);
1538 slightly_bad_write
= dconf_changeset_new_write ("/locked", g_variant_new_string ("value"));
1539 dconf_changeset_set (slightly_bad_write
, "/to-reset", NULL
);
1541 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1543 success
= dconf_engine_change_sync (engine
, empty
, &tag
, &error
);
1544 g_assert_no_error (error
);
1548 success
= dconf_engine_change_sync (engine
, empty
, NULL
, &error
);
1549 g_assert_no_error (error
);
1552 success
= dconf_engine_change_sync (engine
, bad_write
, &tag
, &error
);
1553 g_assert_error (error
, DCONF_ERROR
, DCONF_ERROR_NOT_WRITABLE
);
1554 g_clear_error (&error
);
1555 g_assert (!success
);
1557 success
= dconf_engine_change_sync (engine
, slightly_bad_write
, NULL
, &error
);
1558 g_assert_error (error
, DCONF_ERROR
, DCONF_ERROR_NOT_WRITABLE
);
1559 g_clear_error (&error
);
1560 g_assert (!success
);
1562 /* Up to now, no D-Bus traffic should have been sent at all because we
1563 * only had trivial and non-writable attempts.
1565 * Now try some working cases
1567 dconf_mock_dbus_sync_call_handler
= handle_write_request
;
1568 change_sync_result
= g_variant_new ("(s)", "mytag");
1570 success
= dconf_engine_change_sync (engine
, good_write
, &tag
, &error
);
1571 g_assert_no_error (error
);
1573 g_assert_cmpstr (tag
, ==, "mytag");
1575 change_sync_result
= NULL
;
1577 change_sync_error
= g_error_new_literal (G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "something failed");
1578 success
= dconf_engine_change_sync (engine
, very_good_write
, &tag
, &error
);
1579 g_assert_error (error
, G_FILE_ERROR
, G_FILE_ERROR_NOENT
);
1580 g_assert (!success
);
1581 g_clear_error (&error
);
1582 change_sync_error
= NULL
;
1584 dconf_changeset_unref (empty
);
1585 dconf_changeset_unref (good_write
);
1586 dconf_changeset_unref (very_good_write
);
1587 dconf_changeset_unref (bad_write
);
1588 dconf_changeset_unref (slightly_bad_write
);
1589 dconf_engine_unref (engine
);
1593 send_signal (GBusType type
,
1596 const gchar
*signame
,
1601 value
= g_variant_ref_sink (g_variant_new_parsed (args
));
1602 dconf_engine_handle_dbus_signal (type
, name
, path
, signame
, value
);
1603 g_variant_unref (value
);
1609 DConfEngine
*engine
;
1611 change_log
= g_string_new (NULL
);
1613 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1615 /* Throw some non-sense at it to make sure it gets rejected */
1617 /* Invalid signal name */
1618 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "UnNotify", "('/', [''], 'tag')");
1619 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/site", "UnNotify", "('/', [''], 'tag')");
1620 g_assert_cmpstr (change_log
->str
, ==, "");
1622 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/use", "Notify", "('/', [''], 'tag')");
1623 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/use", "WritabilityNotify", "('/',)");
1624 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/sit", "Notify", "('/', [''], 'tag')");
1625 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/sit", "WritabilityNotify", "('/',)");
1626 g_assert_cmpstr (change_log
->str
, ==, "");
1627 /* Wrong signature for signal */
1628 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/',)");
1629 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('/', [''], '')");
1630 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/site", "Notify", "('/',)");
1631 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/site", "WritabilityNotify", "('/', [''], '')");
1632 g_assert_cmpstr (change_log
->str
, ==, "");
1633 /* Signal delivered on wrong bus type */
1634 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/', [''], 'tag')");
1635 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/site", "Notify", "('/', [''], 'tag')");
1636 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('/',)");
1637 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/site", "WritabilityNotify", "('/',)");
1638 g_assert_cmpstr (change_log
->str
, ==, "");
1639 /* Empty changeset */
1640 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/a', @as [], 'tag')");
1641 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/a/', @as [], 'tag')");
1642 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/site", "Notify", "('/a', @as [], 'tag')");
1643 send_signal (G_BUS_TYPE_SYSTEM
, ":1.123", "/ca/desrt/dconf/Writer/site", "Notify", "('/a/', @as [], 'tag')");
1644 /* Try to notify on some invalid paths to make sure they get properly
1645 * rejected by the engine and not passed onto the user...
1647 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('', [''], 'tag')");
1648 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('a', [''], 'tag')");
1649 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('a/', [''], 'tag')");
1650 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/b//a/', [''], 'tag')");
1651 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/b//a', [''], 'tag')");
1652 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('',)");
1653 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('a',)");
1654 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('a/',)");
1655 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('/b//a/',)");
1656 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('/b//a',)");
1657 g_assert_cmpstr (change_log
->str
, ==, "");
1658 /* Invalid gluing of segments: '/a' + 'b' != '/ab' */
1659 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/a', ['b'], 'tag')");
1660 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/a', ['b', 'c'], 'tag')");
1661 g_assert_cmpstr (change_log
->str
, ==, "");
1662 /* Also: '/a' + '/b' != '/a/b' */
1663 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/a', ['/b'], 'tag')");
1664 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/a', ['', '/b'], 'tag')");
1665 g_assert_cmpstr (change_log
->str
, ==, "");
1666 /* Invalid (non-relative) changes */
1667 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/', ['/'], 'tag')");
1668 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/', ['/a'], 'tag')");
1669 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/', ['a', '/a'], 'tag')");
1670 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify", "('/', ['a', 'a//b'], 'tag')");
1671 g_assert_cmpstr (change_log
->str
, ==, "");
1673 /* Now try some real cases */
1674 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify",
1675 "('/', [''], 'tag')");
1676 g_assert_cmpstr (change_log
->str
, ==, "/:1::tag;");
1677 g_string_set_size (change_log
, 0);
1678 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify",
1679 "('/one/key', [''], 'tag')");
1680 g_assert_cmpstr (change_log
->str
, ==, "/one/key:1::tag;");
1681 g_string_set_size (change_log
, 0);
1682 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify",
1683 "('/two/', ['keys', 'here'], 'tag')");
1684 g_assert_cmpstr (change_log
->str
, ==, "/two/:2:keys,here:tag;");
1685 g_string_set_size (change_log
, 0);
1686 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "Notify",
1687 "('/some/path/', ['a', 'b/', 'c/d'], 'tag')");
1688 g_assert_cmpstr (change_log
->str
, ==, "/some/path/:3:a,b/,c/d:tag;");
1689 g_string_set_size (change_log
, 0);
1690 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('/other/key',)");
1691 g_assert_cmpstr (change_log
->str
, ==, "w:/other/key:1::;");
1692 g_string_set_size (change_log
, 0);
1693 send_signal (G_BUS_TYPE_SESSION
, ":1.123", "/ca/desrt/dconf/Writer/user", "WritabilityNotify", "('/other/dir/',)");
1694 g_assert_cmpstr (change_log
->str
, ==, "w:/other/dir/:1::;");
1695 g_string_set_size (change_log
, 0);
1697 dconf_engine_unref (engine
);
1700 static gboolean it_is_good_to_be_done
;
1703 waiter_thread (gpointer user_data
)
1705 DConfEngine
*engine
= user_data
;
1707 dconf_engine_sync (engine
);
1709 g_assert (g_atomic_int_get (&it_is_good_to_be_done
));
1717 GThread
*waiter_threads
[5];
1718 DConfChangeset
*change
;
1719 DConfEngine
*engine
;
1720 GError
*error
= NULL
;
1724 engine
= dconf_engine_new (SRCDIR
"/profile/dos", NULL
, NULL
);
1726 /* Make sure a waiter thread returns straight away if nothing is
1729 g_atomic_int_set (&it_is_good_to_be_done
, TRUE
);
1730 g_thread_join (g_thread_new ("waiter", waiter_thread
, engine
));
1731 g_atomic_int_set (&it_is_good_to_be_done
, FALSE
);
1733 /* The write will try to check the system-db for a lock. That will
1734 * fail because it doesn't exist...
1736 g_test_expect_message ("dconf", G_LOG_LEVEL_WARNING
, "*unable to open file*");
1737 change
= dconf_changeset_new_write ("/value", g_variant_new_boolean (TRUE
));
1738 success
= dconf_engine_change_fast (engine
, change
, NULL
, &error
);
1739 g_assert_no_error (error
);
1742 /* Spin up some waiters */
1743 for (i
= 0; i
< G_N_ELEMENTS (waiter_threads
); i
++)
1744 waiter_threads
[i
] = g_thread_new ("test waiter", waiter_thread
, engine
);
1745 g_usleep(100 * G_TIME_SPAN_MILLISECOND
);
1746 /* Release them by completing the pending async call */
1747 g_atomic_int_set (&it_is_good_to_be_done
, TRUE
);
1748 dconf_mock_dbus_async_reply (g_variant_new ("(s)", "tag"), NULL
);
1749 /* Make sure they all quit by joining them */
1750 for (i
= 0; i
< G_N_ELEMENTS (waiter_threads
); i
++)
1751 g_thread_join (waiter_threads
[i
]);
1752 g_atomic_int_set (&it_is_good_to_be_done
, FALSE
);
1754 /* Do the same again, but with a failure as a result */
1755 success
= dconf_engine_change_fast (engine
, change
, NULL
, &error
);
1756 g_assert_no_error (error
);
1758 for (i
= 0; i
< G_N_ELEMENTS (waiter_threads
); i
++)
1759 waiter_threads
[i
] = g_thread_new ("test waiter", waiter_thread
, engine
);
1760 g_usleep(100 * G_TIME_SPAN_MILLISECOND
);
1761 error
= g_error_new_literal (G_FILE_ERROR
, G_FILE_ERROR_NOENT
, "some error");
1762 g_test_expect_message ("dconf", G_LOG_LEVEL_WARNING
, "failed to commit changes to dconf: some error");
1763 g_atomic_int_set (&it_is_good_to_be_done
, TRUE
);
1764 dconf_mock_dbus_async_reply (NULL
, error
);
1765 g_clear_error (&error
);
1766 /* Make sure they all quit by joining them */
1767 for (i
= 0; i
< G_N_ELEMENTS (waiter_threads
); i
++)
1768 g_thread_join (waiter_threads
[i
]);
1769 g_atomic_int_set (&it_is_good_to_be_done
, FALSE
);
1771 /* Now put two changes in the queue and make sure we have to reply to
1772 * both of them before the waiters finish.
1774 success
= dconf_engine_change_fast (engine
, change
, NULL
, &error
);
1775 g_assert_no_error (error
);
1777 success
= dconf_engine_change_fast (engine
, change
, NULL
, &error
);
1778 g_assert_no_error (error
);
1780 for (i
= 0; i
< G_N_ELEMENTS (waiter_threads
); i
++)
1781 waiter_threads
[i
] = g_thread_new ("test waiter", waiter_thread
, engine
);
1782 g_usleep(100 * G_TIME_SPAN_MILLISECOND
);
1783 dconf_mock_dbus_async_reply (g_variant_new ("(s)", "tag1"), NULL
);
1784 /* Still should not have quit yet... wait a bit to let the waiters try
1785 * to shoot themselves in their collective feet...
1787 g_usleep(100 * G_TIME_SPAN_MILLISECOND
);
1788 /* Will be OK after the second reply */
1789 g_atomic_int_set (&it_is_good_to_be_done
, TRUE
);
1790 dconf_mock_dbus_async_reply (g_variant_new ("(s)", "tag2"), NULL
);
1791 /* Make sure they all quit by joining them */
1792 for (i
= 0; i
< G_N_ELEMENTS (waiter_threads
); i
++)
1793 g_thread_join (waiter_threads
[i
]);
1794 g_atomic_int_set (&it_is_good_to_be_done
, FALSE
);
1796 dconf_changeset_unref (change
);
1797 dconf_engine_unref (engine
);
1798 dconf_mock_shm_reset ();
1803 main (int argc
, char **argv
)
1805 g_setenv ("XDG_RUNTIME_DIR", "/RUNTIME/", TRUE
);
1806 g_setenv ("XDG_CONFIG_HOME", "/HOME/.config", TRUE
);
1807 g_unsetenv ("DCONF_PROFILE");
1809 main_thread
= g_thread_self ();
1811 g_test_init (&argc
, &argv
, NULL
);
1813 g_test_add_func ("/engine/profile-parser", test_profile_parser
);
1814 g_test_add_func ("/engine/signal-threadsafety", test_signal_threadsafety
);
1815 g_test_add_func ("/engine/sources/user", test_user_source
);
1816 g_test_add_func ("/engine/sources/system", test_system_source
);
1817 g_test_add_func ("/engine/sources/file", test_file_source
);
1818 g_test_add_func ("/engine/sources/service", test_service_source
);
1819 g_test_add_func ("/engine/read", test_read
);
1820 g_test_add_func ("/engine/watch/fast", test_watch_fast
);
1821 g_test_add_func ("/engine/watch/sync", test_watch_sync
);
1822 g_test_add_func ("/engine/watch/watching", test_watching
);
1823 g_test_add_func ("/engine/change/fast", test_change_fast
);
1824 g_test_add_func ("/engine/change/sync", test_change_sync
);
1825 g_test_add_func ("/engine/signals", test_signals
);
1826 g_test_add_func ("/engine/sync", test_sync
);
1828 return g_test_run ();