1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
From: Philip Withnall <pwithnall@gnome.org>
Date: Fri, 19 Sep 2025 13:32:15 +0100
Subject: tests: Add a test for g_reload_user_special_dirs_cache()
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
This test specifically checks whether the documented behaviour of
deliberately leaking old special dirs strings (which might still be
pointed to in user code) works.
I haven’t gone back and used this new unit test with an older version of
GLib, but I suspect the ‘deliberate leak’ code hasn’t worked for a
while. See the changes in the previous few commits, which were necessary
to get this unit test to pass.
The previous `test_user_special_dirs()` test has been deleted, as what
it was testing has been entirely subsumed into the new test.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Origin: upstream, 2.86.1, commit:25dd814ed8e11cea7b509cacd04fb49459f3ef13
---
glib/tests/utils.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 76 insertions(+), 13 deletions(-)
diff --git a/glib/tests/utils.c b/glib/tests/utils.c
index 157e5b7..2d0b2ed 100644
--- a/glib/tests/utils.c
+++ b/glib/tests/utils.c
@@ -819,18 +819,6 @@ test_hostname (void)
g_assert_true (g_utf8_validate (name, -1, NULL));
}
-static void
-test_user_special_dirs (void)
-{
- const gchar *dir, *dir2;
-
- dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
- g_reload_user_special_dirs_cache ();
- dir2 = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
-
- g_assert_cmpstr (dir, ==, dir2);
-}
-
static void
test_user_special_dirs_desktop (void)
{
@@ -924,6 +912,81 @@ test_user_special_dirs_load_unlocked (void)
#endif
}
+static void
+test_user_special_dirs_reload_leaks (void)
+{
+#ifndef USES_USER_DIRS_DIRS
+ g_test_skip ("The user-dirs.dirs parser is not used on this platform.");
+#else
+ g_test_summary ("Tests that old user special dirs values are deliberately leaked on reload.");
+
+ if (g_test_subprocess ())
+ {
+ const char *original_special_dirs[G_USER_N_DIRECTORIES] = { NULL, };
+ const char *new_special_dirs[G_USER_N_DIRECTORIES] = { NULL, };
+ size_t i;
+
+ /* Set some original values for the variables and store them all. */
+ set_mock_user_dirs_dirs_file ("XDG_DESKTOP_DIR = \"/original/desktop/dir\"\n"
+ "XDG_DOCUMENTS_DIR = \"/original/documents/dir\"\n"
+ "XDG_DOWNLOAD_DIR = \"/original/download/dir\"\n");
+ g_reload_user_special_dirs_cache ();
+
+ for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+ original_special_dirs[(GUserDirectory) i] = g_get_user_special_dir ((GUserDirectory) i);
+
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_DESKTOP], ==, "/original/desktop/dir");
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_DOCUMENTS], ==, "/original/documents/dir");
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_DOWNLOAD], ==, "/original/download/dir");
+
+ /* Update the values and reload them. Change some, keep others the same, drop some. */
+ set_mock_user_dirs_dirs_file ("XDG_DESKTOP_DIR = \"/new/desktop/dir\"\n"
+ "XDG_DOCUMENTS_DIR = \"/original/documents/dir\"\n"
+ "XDG_MUSIC_DIR = \"/new/music/dir\"\n");
+ g_reload_user_special_dirs_cache ();
+
+ for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+ new_special_dirs[(GUserDirectory) i] = g_get_user_special_dir ((GUserDirectory) i);
+
+ /* We expect all the original strings to still be accessible. Those which
+ * have the same string values as the new ones will not have changed. The
+ * ones which have changed string value should have their original values
+ * deliberately leaked so that const pointers in the program don’t break.
+ * See the documentation for g_reload_user_special_dirs_cache(). */
+ for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+ g_test_message ("Special dir %" G_GSIZE_FORMAT ", original value %s, new value %s",
+ i, original_special_dirs[(GUserDirectory) i],
+ new_special_dirs[(GUserDirectory) i]);
+
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_DESKTOP], ==, "/original/desktop/dir");
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_DOCUMENTS], ==, "/original/documents/dir");
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_DOWNLOAD], ==, "/original/download/dir");
+ g_assert_cmpstr (original_special_dirs[G_USER_DIRECTORY_MUSIC], ==, NULL);
+ g_assert_cmpstr (new_special_dirs[G_USER_DIRECTORY_DESKTOP], ==, "/new/desktop/dir");
+ g_assert_cmpstr (new_special_dirs[G_USER_DIRECTORY_DOCUMENTS], ==, "/original/documents/dir");
+ g_assert_cmpstr (new_special_dirs[G_USER_DIRECTORY_DOWNLOAD], ==, NULL);
+ g_assert_cmpstr (new_special_dirs[G_USER_DIRECTORY_MUSIC], ==, "/new/music/dir");
+
+ /* We expect exactly these two strings to leak. Rather than mark them as
+ * leaked (which we can do for asan, but not for valgrind at runtime), go
+ * ahead and free them. This means we can catch unexpected additional
+ * leaks, and also unexpected double-frees.
+ *
+ * This is definitely *not* something that production code should be doing.
+ *
+ * We can (relatively) safely do it here because this test is running in
+ * a subprocess which is about to terminate. */
+ g_free ((char *) original_special_dirs[G_USER_DIRECTORY_DESKTOP]);
+ g_free ((char *) original_special_dirs[G_USER_DIRECTORY_DOWNLOAD]);
+ }
+ else
+ {
+ g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT);
+ g_test_trap_assert_passed ();
+ }
+#endif
+}
+
static void
test_os_info (void)
{
@@ -1388,9 +1451,9 @@ main (int argc,
g_test_add_func ("/utils/username", test_username);
g_test_add_func ("/utils/realname", test_realname);
g_test_add_func ("/utils/hostname", test_hostname);
- g_test_add_func ("/utils/user-special-dirs", test_user_special_dirs);
g_test_add_func ("/utils/user-special-dirs/desktop", test_user_special_dirs_desktop);
g_test_add_func ("/utils/user-special-dirs/load-unlocked", test_user_special_dirs_load_unlocked);
+ g_test_add_func ("/utils/user-special-dirs/reload-leaks", test_user_special_dirs_reload_leaks);
g_test_add_func ("/utils/os-info", test_os_info);
g_test_add_func ("/utils/clear-pointer", test_clear_pointer);
g_test_add_func ("/utils/clear-pointer-cast", test_clear_pointer_cast);
|