From b4479d97d5e97eebd4fab39358782a4b78f54814 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 29 Nov 2018 21:57:54 +0000 Subject: [PATCH 01/32] Revert "Meson: appinfo tests are racy if run in parallel" This partially reverts commit 27b5fb589298d17d22ea32634a6d52b73160d0d0. The infrastructure for disabling a test is kept, but the appinfo and desktop-app-info tests no longer need to be run serially. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/1601 --- gio/tests/meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 091733cd1..63e21bdbc 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -156,11 +156,9 @@ if host_machine.system() != 'windows' gio_tests += { 'appinfo' : { 'install' : false, - 'is_parallel' : false, }, 'desktop-app-info' : { 'install' : false, - 'is_parallel' : false, }, } endif From a67eadbdc3d8a317c41cfb011d88de41b271194f Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 10:49:48 +0000 Subject: [PATCH 02/32] gstrfuncs: Add g_strv_equal() This is a utility function which I find myself writing in a number of places. Mostly in unit tests. Signed-off-by: Philip Withnall --- docs/reference/glib/glib-sections.txt | 1 + glib/gstrfuncs.c | 34 +++++++++++++++++++++++++++ glib/gstrfuncs.h | 4 ++++ glib/tests/shell.c | 17 +------------- glib/tests/strfuncs.c | 29 +++++++++++++++++++++++ 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 6f8f906ac..2d3c7f65a 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -1544,6 +1544,7 @@ g_strjoinv GStrv g_strv_length g_strv_contains +g_strv_equal g_strerror diff --git a/glib/gstrfuncs.c b/glib/gstrfuncs.c index 5a9ac884f..756dbc5bc 100644 --- a/glib/gstrfuncs.c +++ b/glib/gstrfuncs.c @@ -3181,6 +3181,40 @@ g_strv_contains (const gchar * const *strv, return FALSE; } +/** + * g_strv_equal: + * @strv1: a %NULL-terminated array of strings + * @strv2: another %NULL-terminated array of strings + * + * Checks if @strv1 and @strv2 contain exactly the same elements in exactly the + * same order. Elements are compared using g_str_equal(). To match independently + * of order, sort the arrays first (using g_qsort_with_data() or similar). + * + * Two empty arrays are considered equal. Neither @strv1 not @strv2 may be + * %NULL. + * + * Returns: %TRUE if @strv1 and @strv2 are equal + * Since: 2.60 + */ +gboolean +g_strv_equal (const gchar * const *strv1, + const gchar * const *strv2) +{ + g_return_val_if_fail (strv1 != NULL, FALSE); + g_return_val_if_fail (strv2 != NULL, FALSE); + + if (strv1 == strv2) + return TRUE; + + for (; *strv1 != NULL && *strv2 != NULL; strv1++, strv2++) + { + if (!g_str_equal (*strv1, *strv2)) + return FALSE; + } + + return (*strv1 == NULL && *strv2 == NULL); +} + static gboolean str_has_sign (const gchar *str) { diff --git a/glib/gstrfuncs.h b/glib/gstrfuncs.h index 6b6ce52f9..fc88cc1c5 100644 --- a/glib/gstrfuncs.h +++ b/glib/gstrfuncs.h @@ -307,6 +307,10 @@ GLIB_AVAILABLE_IN_2_44 gboolean g_strv_contains (const gchar * const *strv, const gchar *str); +GLIB_AVAILABLE_IN_2_60 +gboolean g_strv_equal (const gchar * const *strv1, + const gchar * const *strv2); + /* Convenience ASCII string to number API */ /** diff --git a/glib/tests/shell.c b/glib/tests/shell.c index c3cab105a..1ed356e15 100644 --- a/glib/tests/shell.c +++ b/glib/tests/shell.c @@ -68,21 +68,6 @@ static CmdlineTest cmdline_tests[] = {"foo '/bar/summer'\\''09 tours.pdf'", 2, {"foo", "/bar/summer'09 tours.pdf", NULL}, -1} }; -static gboolean -strv_equal (gchar **a, gchar **b) -{ - gint i; - - if (g_strv_length (a) != g_strv_length (b)) - return FALSE; - - for (i = 0; a[i]; i++) - if (g_strcmp0 (a[i], b[i]) != 0) - return FALSE; - - return TRUE; -} - static void do_cmdline_test (gconstpointer d) { @@ -99,7 +84,7 @@ do_cmdline_test (gconstpointer d) { g_assert (res); g_assert_cmpint (argc, ==, test->argc); - g_assert (strv_equal (argv, (gchar **)test->argv)); + g_assert (g_strv_equal ((const gchar * const *) argv, (const gchar * const *) test->argv)); g_assert_no_error (err); } else diff --git a/glib/tests/strfuncs.c b/glib/tests/strfuncs.c index a2d059009..7c74cdc5e 100644 --- a/glib/tests/strfuncs.c +++ b/glib/tests/strfuncs.c @@ -1507,6 +1507,34 @@ test_strv_contains (void) g_assert_false (g_strv_contains (strv_empty, "")); } +/* Test g_strv_equal() works for various inputs. */ +static void +test_strv_equal (void) +{ + const gchar *strv_empty[] = { NULL }; + const gchar *strv_empty2[] = { NULL }; + const gchar *strv_simple[] = { "hello", "you", NULL }; + const gchar *strv_simple2[] = { "hello", "you", NULL }; + const gchar *strv_simple_reordered[] = { "you", "hello", NULL }; + const gchar *strv_simple_superset[] = { "hello", "you", "again", NULL }; + const gchar *strv_another[] = { "not", "a", "coded", "message", NULL }; + + g_assert_true (g_strv_equal (strv_empty, strv_empty)); + g_assert_true (g_strv_equal (strv_empty, strv_empty2)); + g_assert_true (g_strv_equal (strv_empty2, strv_empty)); + g_assert_false (g_strv_equal (strv_empty, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_empty)); + g_assert_true (g_strv_equal (strv_simple, strv_simple)); + g_assert_true (g_strv_equal (strv_simple, strv_simple2)); + g_assert_true (g_strv_equal (strv_simple2, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_simple_reordered)); + g_assert_false (g_strv_equal (strv_simple_reordered, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_simple_superset)); + g_assert_false (g_strv_equal (strv_simple_superset, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_another)); + g_assert_false (g_strv_equal (strv_another, strv_simple)); +} + typedef enum { SIGNED, @@ -1761,6 +1789,7 @@ main (int argc, g_test_add_func ("/strfuncs/strup", test_strup); g_test_add_func ("/strfuncs/transliteration", test_transliteration); g_test_add_func ("/strfuncs/strv-contains", test_strv_contains); + g_test_add_func ("/strfuncs/strv-equal", test_strv_equal); g_test_add_func ("/strfuncs/ascii-string-to-num/usual", test_ascii_string_to_number_usual); g_test_add_func ("/strfuncs/ascii-string-to-num/pathological", test_ascii_string_to_number_pathological); From e6eb4869ba2354484478834d5af86fc91c7bc107 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 11:04:09 +0000 Subject: [PATCH 03/32] gutils: Refactor initialisation of XDG variables Split out the code which calculates each XDG variable value from the code which caches it, so that GLib can internally recalculate the variables if needed, without necessarily trashing the user-visible cache. This will be useful in a following commit to add support for explicitly reloading the variables. This commit necessarily reworks how g_get_user_runtime_dir() is structured, since it was inexplicably structured differently from (but equivalently to) the other XDG variable functions. Future refactoring could easily share a lot more code between these g_build_*() functions. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib.supp | 20 +++ glib/gutils.c | 342 ++++++++++++++++++++++++++------------------------ 2 files changed, 196 insertions(+), 166 deletions(-) diff --git a/glib.supp b/glib.supp index 9163ee3e1..c8f3682ef 100644 --- a/glib.supp +++ b/glib.supp @@ -543,3 +543,23 @@ ... fun:g_object_new_valist } + +# g_get_system_data_dirs() caches a one-time allocation +{ + g_get_system_data_dirs + Memcheck:Leak + fun:malloc + ... + fun:g_build_system_data_dirs + fun:g_get_system_data_dirs +} + +# g_get_user_data_dir() caches a one-time allocation +{ + g_get_user_data_dir + Memcheck:Leak + fun:realloc + ... + fun:g_build_user_data_dir + fun:g_get_user_data_dir +} \ No newline at end of file diff --git a/glib/gutils.c b/glib/gutils.c index e86aeca32..ebc244e57 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -559,6 +559,7 @@ static gchar *g_user_data_dir = NULL; static gchar **g_system_data_dirs = NULL; static gchar *g_user_cache_dir = NULL; static gchar *g_user_config_dir = NULL; +static gchar *g_user_runtime_dir = NULL; static gchar **g_system_config_dirs = NULL; static gchar **g_user_special_dirs = NULL; @@ -1167,6 +1168,31 @@ g_set_application_name (const gchar *application_name) g_warning ("g_set_application_name() called multiple times"); } +static gchar * +g_build_user_data_dir (void) +{ + gchar *data_dir = NULL; + const gchar *data_dir_env = g_getenv ("XDG_DATA_HOME"); + + if (data_dir_env && data_dir_env[0]) + data_dir = g_strdup (data_dir_env); +#ifdef G_OS_WIN32 + else + data_dir = get_special_folder (CSIDL_LOCAL_APPDATA); +#endif + if (!data_dir || !data_dir[0]) + { + const gchar *home_dir = g_get_home_dir (); + + if (home_dir) + data_dir = g_build_filename (home_dir, ".local", "share", NULL); + else + data_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".local", "share", NULL); + } + + return g_steal_pointer (&data_dir); +} + /** * g_get_user_data_dir: * @@ -1192,67 +1218,39 @@ g_set_application_name (const gchar *application_name) const gchar * g_get_user_data_dir (void) { - gchar *data_dir = NULL; - G_LOCK (g_utils_global); - if (!g_user_data_dir) - { - const gchar *data_dir_env = g_getenv ("XDG_DATA_HOME"); - - if (data_dir_env && data_dir_env[0]) - data_dir = g_strdup (data_dir_env); -#ifdef G_OS_WIN32 - else - data_dir = get_special_folder (CSIDL_LOCAL_APPDATA); -#endif - if (!data_dir || !data_dir[0]) - { - const gchar *home_dir = g_get_home_dir (); - - if (home_dir) - data_dir = g_build_filename (home_dir, ".local", "share", NULL); - else - data_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".local", "share", NULL); - } - - g_user_data_dir = data_dir; - } - else - data_dir = g_user_data_dir; + if (g_user_data_dir == NULL) + g_user_data_dir = g_build_user_data_dir (); G_UNLOCK (g_utils_global); - return data_dir; + return g_user_data_dir; } -static void -g_init_user_config_dir (void) +static gchar * +g_build_user_config_dir (void) { gchar *config_dir = NULL; + const gchar *config_dir_env = g_getenv ("XDG_CONFIG_HOME"); - if (!g_user_config_dir) - { - const gchar *config_dir_env = g_getenv ("XDG_CONFIG_HOME"); - - if (config_dir_env && config_dir_env[0]) - config_dir = g_strdup (config_dir_env); + if (config_dir_env && config_dir_env[0]) + config_dir = g_strdup (config_dir_env); #ifdef G_OS_WIN32 - else - config_dir = get_special_folder (CSIDL_LOCAL_APPDATA); + else + config_dir = get_special_folder (CSIDL_LOCAL_APPDATA); #endif - if (!config_dir || !config_dir[0]) - { - const gchar *home_dir = g_get_home_dir (); + if (!config_dir || !config_dir[0]) + { + const gchar *home_dir = g_get_home_dir (); - if (home_dir) - config_dir = g_build_filename (home_dir, ".config", NULL); - else - config_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".config", NULL); - } - - g_user_config_dir = config_dir; + if (home_dir) + config_dir = g_build_filename (home_dir, ".config", NULL); + else + config_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".config", NULL); } + + return g_steal_pointer (&config_dir); } /** @@ -1282,13 +1280,39 @@ g_get_user_config_dir (void) { G_LOCK (g_utils_global); - g_init_user_config_dir (); + if (g_user_config_dir == NULL) + g_user_config_dir = g_build_user_config_dir (); G_UNLOCK (g_utils_global); return g_user_config_dir; } +static gchar * +g_build_user_cache_dir (void) +{ + gchar *cache_dir = NULL; + const gchar *cache_dir_env = g_getenv ("XDG_CACHE_HOME"); + + if (cache_dir_env && cache_dir_env[0]) + cache_dir = g_strdup (cache_dir_env); +#ifdef G_OS_WIN32 + else + cache_dir = get_special_folder (CSIDL_INTERNET_CACHE); +#endif + if (!cache_dir || !cache_dir[0]) + { + const gchar *home_dir = g_get_home_dir (); + + if (home_dir) + cache_dir = g_build_filename (home_dir, ".cache", NULL); + else + cache_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".cache", NULL); + } + + return g_steal_pointer (&cache_dir); +} + /** * g_get_user_cache_dir: * @@ -1313,37 +1337,42 @@ g_get_user_config_dir (void) const gchar * g_get_user_cache_dir (void) { - gchar *cache_dir = NULL; - G_LOCK (g_utils_global); - if (!g_user_cache_dir) - { - const gchar *cache_dir_env = g_getenv ("XDG_CACHE_HOME"); - - if (cache_dir_env && cache_dir_env[0]) - cache_dir = g_strdup (cache_dir_env); -#ifdef G_OS_WIN32 - else - cache_dir = get_special_folder (CSIDL_INTERNET_CACHE); -#endif - if (!cache_dir || !cache_dir[0]) - { - const gchar *home_dir = g_get_home_dir (); - - if (home_dir) - cache_dir = g_build_filename (home_dir, ".cache", NULL); - else - cache_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".cache", NULL); - } - g_user_cache_dir = cache_dir; - } - else - cache_dir = g_user_cache_dir; + if (g_user_cache_dir == NULL) + g_user_cache_dir = g_build_user_cache_dir (); G_UNLOCK (g_utils_global); - return cache_dir; + return g_user_cache_dir; +} + +static gchar * +g_build_user_runtime_dir (void) +{ + gchar *runtime_dir = NULL; + const gchar *runtime_dir_env = g_getenv ("XDG_RUNTIME_DIR"); + + if (runtime_dir_env && runtime_dir_env[0]) + runtime_dir = g_strdup (runtime_dir_env); + else + { + runtime_dir = g_build_user_cache_dir (); + + /* The user should be able to rely on the directory existing + * when the function returns. Probably it already does, but + * let's make sure. Just do mkdir() directly since it will be + * no more expensive than a stat() in the case that the + * directory already exists and is a lot easier. + * + * $XDG_CACHE_HOME is probably ~/.cache/ so as long as $HOME + * exists this will work. If the user changed $XDG_CACHE_HOME + * then they can make sure that it exists... + */ + (void) g_mkdir (runtime_dir, 0700); + } + + return g_steal_pointer (&runtime_dir); } /** @@ -1368,38 +1397,14 @@ g_get_user_cache_dir (void) const gchar * g_get_user_runtime_dir (void) { - static const gchar *runtime_dir; + G_LOCK (g_utils_global); - if (g_once_init_enter (&runtime_dir)) - { - const gchar *dir; + if (g_user_runtime_dir == NULL) + g_user_runtime_dir = g_build_user_runtime_dir (); - dir = g_strdup (getenv ("XDG_RUNTIME_DIR")); + G_UNLOCK (g_utils_global); - if (dir == NULL) - { - /* No need to strdup this one since it is valid forever. */ - dir = g_get_user_cache_dir (); - - /* The user should be able to rely on the directory existing - * when the function returns. Probably it already does, but - * let's make sure. Just do mkdir() directly since it will be - * no more expensive than a stat() in the case that the - * directory already exists and is a lot easier. - * - * $XDG_CACHE_HOME is probably ~/.cache/ so as long as $HOME - * exists this will work. If the user changed $XDG_CACHE_HOME - * then they can make sure that it exists... - */ - (void) g_mkdir (dir, 0700); - } - - g_assert (dir != NULL); - - g_once_init_leave (&runtime_dir, dir); - } - - return runtime_dir; + return g_user_runtime_dir; } #ifdef HAVE_CARBON @@ -1553,16 +1558,18 @@ load_user_special_dirs (void) static void load_user_special_dirs (void) { + gchar *config_dir = NULL; gchar *config_file; gchar *data; gchar **lines; gint n_lines, i; - g_init_user_config_dir (); - config_file = g_build_filename (g_user_config_dir, + config_dir = g_build_user_config_dir (); + config_file = g_build_filename (config_dir, "user-dirs.dirs", NULL); - + g_free (config_dir); + if (!g_file_get_contents (config_file, &data, NULL, NULL)) { g_free (config_file); @@ -1942,7 +1949,7 @@ g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void)) gboolean should_call_g_get_system_data_dirs; should_call_g_get_system_data_dirs = TRUE; - /* These checks are the same as the ones that g_get_system_data_dirs() does. + /* These checks are the same as the ones that g_build_system_data_dirs() does. * Please keep them in sync. */ G_LOCK (g_utils_global); @@ -1986,6 +1993,30 @@ g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void)) #endif +static gchar ** +g_build_system_data_dirs (void) +{ + gchar **data_dir_vector = NULL; + gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS"); + + /* These checks are the same as the ones that g_win32_get_system_data_dirs_for_module() + * does. Please keep them in sync. + */ +#ifndef G_OS_WIN32 + if (!data_dirs || !data_dirs[0]) + data_dirs = "/usr/local/share/:/usr/share/"; + + data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); +#else + if (!data_dirs || !data_dirs[0]) + data_dir_vector = g_strdupv ((gchar **) g_win32_get_system_data_dirs_for_module_real (NULL)); + else + data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); +#endif + + return g_steal_pointer (&data_dir_vector); +} + /** * g_get_system_data_dirs: * @@ -2030,37 +2061,46 @@ g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void)) const gchar * const * g_get_system_data_dirs (void) { - gchar **data_dir_vector; - - /* These checks are the same as the ones that g_win32_get_system_data_dirs_for_module() - * does. Please keep them in sync. - */ G_LOCK (g_utils_global); - if (!g_system_data_dirs) - { - gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS"); - -#ifndef G_OS_WIN32 - if (!data_dirs || !data_dirs[0]) - data_dirs = "/usr/local/share/:/usr/share/"; - - data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); -#else - if (!data_dirs || !data_dirs[0]) - data_dir_vector = g_strdupv ((gchar **) g_win32_get_system_data_dirs_for_module_real (NULL)); - else - data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0); -#endif - - g_system_data_dirs = data_dir_vector; - } - else - data_dir_vector = g_system_data_dirs; + if (g_system_data_dirs == NULL) + g_system_data_dirs = g_build_system_data_dirs (); G_UNLOCK (g_utils_global); - return (const gchar * const *) data_dir_vector; + return (const gchar * const *) g_system_data_dirs; +} + +static gchar ** +g_build_system_config_dirs (void) +{ + gchar **conf_dir_vector = NULL; + const gchar *conf_dirs = g_getenv ("XDG_CONFIG_DIRS"); +#ifdef G_OS_WIN32 + if (conf_dirs) + { + conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); + } + else + { + gchar *special_conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA); + + if (special_conf_dirs) + conf_dir_vector = g_strsplit (special_conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); + else + /* Return empty list */ + conf_dir_vector = g_strsplit ("", G_SEARCHPATH_SEPARATOR_S, 0); + + g_free (special_conf_dirs); + } +#else + if (!conf_dirs || !conf_dirs[0]) + conf_dirs = "/etc/xdg"; + + conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); +#endif + + return g_steal_pointer (&conf_dir_vector); } /** @@ -2093,44 +2133,14 @@ g_get_system_data_dirs (void) const gchar * const * g_get_system_config_dirs (void) { - gchar **conf_dir_vector; - G_LOCK (g_utils_global); - if (!g_system_config_dirs) - { - const gchar *conf_dirs = g_getenv ("XDG_CONFIG_DIRS"); -#ifdef G_OS_WIN32 - if (conf_dirs) - { - conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); - } - else - { - gchar *special_conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA); + if (g_system_config_dirs == NULL) + g_system_config_dirs = g_build_system_config_dirs (); - if (special_conf_dirs) - conf_dir_vector = g_strsplit (special_conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); - else - /* Return empty list */ - conf_dir_vector = g_strsplit ("", G_SEARCHPATH_SEPARATOR_S, 0); - - g_free (special_conf_dirs); - } -#else - if (!conf_dirs || !conf_dirs[0]) - conf_dirs = "/etc/xdg"; - - conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0); -#endif - - g_system_config_dirs = conf_dir_vector; - } - else - conf_dir_vector = g_system_config_dirs; G_UNLOCK (g_utils_global); - return (const gchar * const *) conf_dir_vector; + return (const gchar * const *) g_system_config_dirs; } /** From 00b50d28f95dd9ccafec289e2bae0c7617d15d58 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 11:06:18 +0000 Subject: [PATCH 04/32] gutils: Fix a typo in a documentation comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ‘threadsafety’ isn’t a word. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib/gutils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glib/gutils.c b/glib/gutils.c index ebc244e57..3937ff5e5 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -1696,7 +1696,7 @@ load_user_special_dirs (void) * that the latest on-disk version is used. Call this only * if you just changed the data on disk yourself. * - * Due to threadsafety issues this may cause leaking of strings + * Due to thread safety issues this may cause leaking of strings * that were previously returned from g_get_user_special_dir() * that can't be freed. We ensure to only leak the data for * the directories that actually changed value though. From f27532e7843c528006c57782b839018288aabcce Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 13:06:12 +0000 Subject: [PATCH 05/32] gtestutils: Move a documentation comment to the symbol it documents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seems a bit odd to have the documentation comment miles from what it’s actually documenting. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib/gtestutils.c | 22 ---------------------- glib/gtestutils.h | 20 +++++++++++++++++++- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 42fccacf5..72f62af19 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -3781,25 +3781,3 @@ g_test_get_filename (GTestFileType file_type, return result; } - -/* --- macros docs START --- */ -/** - * g_test_add: - * @testpath: The test path for a new test case. - * @Fixture: The type of a fixture data structure. - * @tdata: Data argument for the test functions. - * @fsetup: The function to set up the fixture data. - * @ftest: The actual test function. - * @fteardown: The function to tear down the fixture data. - * - * Hook up a new test case at @testpath, similar to g_test_add_func(). - * A fixture data structure with setup and teardown functions may be provided, - * similar to g_test_create_case(). - * - * g_test_add() is implemented as a macro, so that the fsetup(), ftest() and - * fteardown() callbacks can expect a @Fixture pointer as their first argument - * in a type safe manner. They otherwise have type #GTestFixtureFunc. - * - * Since: 2.16 - **/ -/* --- macros docs END --- */ diff --git a/glib/gtestutils.h b/glib/gtestutils.h index 4e1293bc8..3028514fb 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -230,7 +230,25 @@ gboolean g_test_failed (void); GLIB_AVAILABLE_IN_2_38 void g_test_set_nonfatal_assertions (void); -/* hook up a test with fixture under test path */ +/** + * g_test_add: + * @testpath: The test path for a new test case. + * @Fixture: The type of a fixture data structure. + * @tdata: Data argument for the test functions. + * @fsetup: The function to set up the fixture data. + * @ftest: The actual test function. + * @fteardown: The function to tear down the fixture data. + * + * Hook up a new test case at @testpath, similar to g_test_add_func(). + * A fixture data structure with setup and teardown functions may be provided, + * similar to g_test_create_case(). + * + * g_test_add() is implemented as a macro, so that the fsetup(), ftest() and + * fteardown() callbacks can expect a @Fixture pointer as their first argument + * in a type safe manner. They otherwise have type #GTestFixtureFunc. + * + * Since: 2.16 + */ #define g_test_add(testpath, Fixture, tdata, fsetup, ftest, fteardown) \ G_STMT_START { \ void (*add_vtable) (const char*, \ From aee897bd89c56584df5341be57f385c5dca51d60 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 13:06:45 +0000 Subject: [PATCH 06/32] gutils: Fix a typo in a comment Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib/gutils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glib/gutils.h b/glib/gutils.h index 84c0f4f20..070f9cff5 100644 --- a/glib/gutils.h +++ b/glib/gutils.h @@ -78,7 +78,7 @@ GLIB_AVAILABLE_IN_ALL const gchar * const * g_get_system_data_dirs (void); #ifdef G_OS_WIN32 -/* This functions is not part of the public GLib API */ +/* This function is not part of the public GLib API */ GLIB_AVAILABLE_IN_ALL const gchar * const * g_win32_get_system_data_dirs_for_module (void (*address_of_function)(void)); #endif From 46f47641c5915fbfc8eeac27f63838cd9a0a7aee Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:20:48 +0000 Subject: [PATCH 07/32] tests: Avoid chdir() call at the start of appinfo tests By encoding the path to the appinfo-test binary in the .desktop files, we can avoid a chdir() call in the tests, which was a bit ugly. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- gio/tests/Makefile.am | 33 +++++++++++-- ....desktop => appinfo-test-gnome.desktop.in} | 2 +- ...sktop => appinfo-test-notgnome.desktop.in} | 2 +- gio/tests/appinfo-test.c | 2 +- ...o-test.desktop => appinfo-test.desktop.in} | 2 +- ...test2.desktop => appinfo-test2.desktop.in} | 2 +- gio/tests/appinfo.c | 48 +++++++++---------- gio/tests/desktop-app-info.c | 11 +---- gio/tests/meson.build | 28 +++++++++-- 9 files changed, 83 insertions(+), 47 deletions(-) rename gio/tests/{appinfo-test-gnome.desktop => appinfo-test-gnome.desktop.in} (64%) rename gio/tests/{appinfo-test-notgnome.desktop => appinfo-test-notgnome.desktop.in} (63%) rename gio/tests/{appinfo-test.desktop => appinfo-test.desktop.in} (83%) rename gio/tests/{appinfo-test2.desktop => appinfo-test2.desktop.in} (81%) diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 15ab66e93..26c2d95b6 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -339,10 +339,6 @@ dist_test_data += \ dist_test_data += \ appinfo-test-actions.desktop \ - appinfo-test-gnome.desktop \ - appinfo-test-notgnome.desktop \ - appinfo-test.desktop \ - appinfo-test2.desktop \ file.c \ org.gtk.test.dbusappinfo.desktop \ x-content/image-dcf/DCIM/Camera/20130831_203925.jpg \ @@ -367,6 +363,35 @@ clean-mimeapps: rm -rf xdgdatadir xdgdatahome xdgconfighome endif +appinfo-test-gnome.desktop: appinfo-test-gnome.desktop.in Makefile + $(AM_V_GEN)$(SED) \ + -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ + $< > $@ +appinfo-test-notgnome.desktop: appinfo-test-notgnome.desktop.in Makefile + $(AM_V_GEN)$(SED) \ + -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ + $< > $@ +appinfo-test.desktop: appinfo-test.desktop.in Makefile + $(AM_V_GEN)$(SED) \ + -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ + $< > $@ +appinfo-test2.desktop: appinfo-test2.desktop.in Makefile + $(AM_V_GEN)$(SED) \ + -e 's|[@]installed_tests_dir[@]|$(installed_testdir)|g' \ + $< > $@ + +appinfo_desktop_templates = \ + appinfo-test-gnome.desktop.in \ + appinfo-test-notgnome.desktop.in \ + appinfo-test.desktop.in \ + appinfo-test2.desktop.in \ + $(NULL) +appinfo_desktop_files = $(appinfo_desktop_templates:.in=) + +EXTRA_DIST += $(appinfo_desktop_templates) +CLEANFILES += $(appinfo_desktop_files) +test_data += $(appinfo_desktop_files) + uninstalled_test_programs += gsettings gschema-compile gsettings_DEPENDENCIES = test.mo CLEANFILES += test.mo de/LC_MESSAGES/test.mo keyfile/gsettings.store diff --git a/gio/tests/appinfo-test-gnome.desktop b/gio/tests/appinfo-test-gnome.desktop.in similarity index 64% rename from gio/tests/appinfo-test-gnome.desktop rename to gio/tests/appinfo-test-gnome.desktop.in index 9e3b42848..e32d545f6 100644 --- a/gio/tests/appinfo-test-gnome.desktop +++ b/gio/tests/appinfo-test-gnome.desktop.in @@ -1,6 +1,6 @@ [Desktop Entry] Type=Application Name=appinfo-test -Exec=./appinfo-test --option +Exec=@installed_tests_dir@/appinfo-test --option OnlyShowIn=GNOME;KDE; NotShowIn=ROX; diff --git a/gio/tests/appinfo-test-notgnome.desktop b/gio/tests/appinfo-test-notgnome.desktop.in similarity index 63% rename from gio/tests/appinfo-test-notgnome.desktop rename to gio/tests/appinfo-test-notgnome.desktop.in index 2f7c11d9c..585818216 100644 --- a/gio/tests/appinfo-test-notgnome.desktop +++ b/gio/tests/appinfo-test-notgnome.desktop.in @@ -1,6 +1,6 @@ [Desktop Entry] Type=Application Name=appinfo-test -Exec=./appinfo-test --option +Exec=@installed_tests_dir@/appinfo-test --option OnlyShowIn=KDE; NotShowIn=GNOME; diff --git a/gio/tests/appinfo-test.c b/gio/tests/appinfo-test.c index 86d0e492c..3a91a2f9b 100644 --- a/gio/tests/appinfo-test.c +++ b/gio/tests/appinfo-test.c @@ -14,7 +14,7 @@ main (int argc, char *argv[]) gchar *expected; gint pid_from_env; - expected = g_test_build_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + expected = g_test_build_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); g_assert_cmpstr (envvar, ==, expected); g_free (expected); diff --git a/gio/tests/appinfo-test.desktop b/gio/tests/appinfo-test.desktop.in similarity index 83% rename from gio/tests/appinfo-test.desktop rename to gio/tests/appinfo-test.desktop.in index 90e412043..bc292b135 100644 --- a/gio/tests/appinfo-test.desktop +++ b/gio/tests/appinfo-test.desktop.in @@ -7,7 +7,7 @@ X-GNOME-FullName=example X-GNOME-FullName[de]=Beispiel Comment=GAppInfo example Comment[de]=GAppInfo Beispiel -Exec=./appinfo-test --option %U %i --name %c --filename %k %m %% +Exec=@installed_tests_dir@/appinfo-test --option %U %i --name %c --filename %k %m %% Icon=testicon.svg Terminal=true StartupNotify=true diff --git a/gio/tests/appinfo-test2.desktop b/gio/tests/appinfo-test2.desktop.in similarity index 81% rename from gio/tests/appinfo-test2.desktop rename to gio/tests/appinfo-test2.desktop.in index 1f5539375..70780f812 100644 --- a/gio/tests/appinfo-test2.desktop +++ b/gio/tests/appinfo-test2.desktop.in @@ -6,6 +6,6 @@ X-GNOME-FullName=example X-GNOME-FullName[de]=Beispiel Comment=GAppInfo example Comment[de]=GAppInfo Beispiel -Exec=./appinfo-test --option +Exec=@installed_tests_dir@/appinfo-test --option TryExec=does-not-exist Icon=testicon diff --git a/gio/tests/appinfo.c b/gio/tests/appinfo.c index 58bba4e1d..12e5a0a7e 100644 --- a/gio/tests/appinfo.c +++ b/gio/tests/appinfo.c @@ -133,7 +133,7 @@ test_launch_for_app_info (GAppInfo *appinfo) g_assert (g_app_info_launch_uris (appinfo, NULL, NULL, &error)); g_assert_no_error (error); - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); file = g_file_new_for_path (path); l = NULL; l = g_list_append (l, file); @@ -144,7 +144,7 @@ test_launch_for_app_info (GAppInfo *appinfo) g_object_unref (file); l = NULL; - uri = g_strconcat ("file://", g_test_get_dir (G_TEST_DIST), "/appinfo-test.desktop", NULL); + uri = g_strconcat ("file://", g_test_get_dir (G_TEST_BUILT), "/appinfo-test.desktop", NULL); l = g_list_append (l, uri); l = g_list_append (l, "file:///etc/group#adm"); @@ -161,7 +161,7 @@ test_launch (Fixture *fixture, GAppInfo *appinfo; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert (appinfo != NULL); @@ -191,13 +191,16 @@ test_launch_no_app_id (Fixture *fixture, "Keywords=keyword1;test keyword;\n" "Categories=GNOME;GTK;\n"; - const char *exec_line_variants[] = { - "Exec=./appinfo-test --option %U %i --name %c --filename %k %m %%", - "Exec=./appinfo-test --option %u %i --name %c --filename %k %m %%" - }; - + gchar *exec_line_variants[2]; gsize i; + exec_line_variants[0] = g_strdup_printf ( + "Exec=%s/appinfo-test --option %%U %%i --name %%c --filename %%k %%m %%%%", + g_test_get_dir (G_TEST_BUILT)); + exec_line_variants[1] = g_strdup_printf ( + "Exec=%s/appinfo-test --option %%u %%i --name %%c --filename %%k %%m %%%%", + g_test_get_dir (G_TEST_BUILT)); + g_test_bug ("791337"); for (i = 0; i < G_N_ELEMENTS (exec_line_variants); i++) @@ -229,6 +232,9 @@ test_launch_no_app_id (Fixture *fixture, g_object_unref (appinfo); g_key_file_unref (fake_desktop_file); } + + g_free (exec_line_variants[1]); + g_free (exec_line_variants[0]); } static void @@ -242,7 +248,7 @@ test_locale (const char *locale) g_setenv ("LANGUAGE", locale, TRUE); setlocale (LC_ALL, ""); - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); if (g_strcmp0 (locale, "C") == 0) @@ -290,7 +296,7 @@ test_basic (Fixture *fixture, GIcon *icon, *icon2; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert_cmpstr (g_app_info_get_id (appinfo), ==, "appinfo-test.desktop"); @@ -317,17 +323,17 @@ test_show_in (Fixture *fixture, GAppInfo *appinfo; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert (g_app_info_should_show (appinfo)); g_object_unref (appinfo); - path = g_test_get_filename (G_TEST_DIST, "appinfo-test-gnome.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test-gnome.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert (g_app_info_should_show (appinfo)); g_object_unref (appinfo); - path = g_test_get_filename (G_TEST_DIST, "appinfo-test-notgnome.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test-notgnome.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert (!g_app_info_should_show (appinfo)); g_object_unref (appinfo); @@ -471,7 +477,7 @@ test_tryexec (Fixture *fixture, GAppInfo *appinfo; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test2.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test2.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert (appinfo == NULL); @@ -596,7 +602,7 @@ test_startup_wm_class (Fixture *fixture, const char *wm_class; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = g_desktop_app_info_new_from_filename (path); wm_class = g_desktop_app_info_get_startup_wm_class (appinfo); @@ -613,7 +619,7 @@ test_supported_types (Fixture *fixture, const char * const *content_types; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = G_APP_INFO (g_desktop_app_info_new_from_filename (path)); content_types = g_app_info_get_supported_types (appinfo); @@ -638,7 +644,7 @@ test_from_keyfile (Fixture *fixture, const gchar *name; const gchar *path; - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); kf = g_key_file_new (); g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, &error); g_assert_no_error (error); @@ -673,19 +679,11 @@ test_from_keyfile (Fixture *fixture, int main (int argc, char *argv[]) { - const gchar *build_dir; - g_setenv ("XDG_CURRENT_DESKTOP", "GNOME", TRUE); g_test_init (&argc, &argv, NULL); g_test_bug_base ("https://bugzilla.gnome.org/show_bug.cgi?id="); - /* With Meson build we need to change into right directory, so that the - * appinfo-test binary can be found. */ - build_dir = g_getenv ("G_TEST_BUILDDIR"); - if (build_dir) - g_chdir (build_dir); - g_test_add ("/appinfo/basic", Fixture, NULL, setup, test_basic, teardown); g_test_add ("/appinfo/text", Fixture, NULL, setup, test_text, teardown); g_test_add ("/appinfo/launch", Fixture, NULL, setup, test_launch, teardown); diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index 4871d85a5..48317967c 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -365,7 +365,7 @@ test_extra_getters (void) g_setenv ("LANGUAGE", "de_DE.UTF8", TRUE); setlocale (LC_ALL, ""); - appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL)); + appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL)); g_assert (appinfo != NULL); g_assert (g_desktop_app_info_has_key (appinfo, "Terminal")); @@ -814,7 +814,7 @@ test_launch_as_manager (void) return; } - path = g_test_get_filename (G_TEST_DIST, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = g_desktop_app_info_new_from_filename (path); g_assert_nonnull (appinfo); @@ -841,15 +841,8 @@ int main (int argc, char *argv[]) { - const gchar *build_dir; gint result; - /* With Meson build we need to change into right directory, so that the - * appinfo-test binary can be found. */ - build_dir = g_getenv ("G_TEST_BUILDDIR"); - if (build_dir) - g_chdir (build_dir); - g_test_init (&argc, &argv, NULL); basedir = g_get_current_dir (); diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 63e21bdbc..a07ddf807 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -395,15 +395,35 @@ if host_machine.system() != 'windows' } endif +appinfo_test_desktop_files = [ + 'appinfo-test-gnome.desktop', + 'appinfo-test-notgnome.desktop', + 'appinfo-test.desktop', + 'appinfo-test2.desktop', +] + +cdata = configuration_data() +if installed_tests_enabled + cdata.set('installed_tests_dir', installed_tests_execdir) +else + cdata.set('installed_tests_dir', meson.current_build_dir()) +endif + +foreach appinfo_test_desktop_file : appinfo_test_desktop_files + configure_file( + input: appinfo_test_desktop_file + '.in', + output: appinfo_test_desktop_file, + install_dir: installed_tests_execdir, + install: installed_tests_enabled, + configuration: cdata, + ) +endforeach + if installed_tests_enabled install_data( 'contexts.c', 'g-icon.c', 'appinfo-test-actions.desktop', - 'appinfo-test-gnome.desktop', - 'appinfo-test-notgnome.desktop', - 'appinfo-test.desktop', - 'appinfo-test2.desktop', 'file.c', 'org.gtk.test.dbusappinfo.desktop', install_dir : installed_tests_execdir, From 8ddfbb308bee72ccd83837614e7a9d88456ac194 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:22:06 +0000 Subject: [PATCH 08/32] tests: Fix indentation of a block in the appinfo test No functional changes. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- gio/tests/appinfo.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/gio/tests/appinfo.c b/gio/tests/appinfo.c index 12e5a0a7e..971b7eb83 100644 --- a/gio/tests/appinfo.c +++ b/gio/tests/appinfo.c @@ -204,34 +204,34 @@ test_launch_no_app_id (Fixture *fixture, g_test_bug ("791337"); for (i = 0; i < G_N_ELEMENTS (exec_line_variants); i++) - { - gchar *desktop_file_contents; - GKeyFile *fake_desktop_file; - GAppInfo *appinfo; - gboolean loaded; + { + gchar *desktop_file_contents; + GKeyFile *fake_desktop_file; + GAppInfo *appinfo; + gboolean loaded; - g_test_message ("Exec line variant #%" G_GSIZE_FORMAT, i); + g_test_message ("Exec line variant #%" G_GSIZE_FORMAT, i); - desktop_file_contents = g_strdup_printf ("%s\n%s", - desktop_file_base_contents, - exec_line_variants[i]); + desktop_file_contents = g_strdup_printf ("%s\n%s", + desktop_file_base_contents, + exec_line_variants[i]); - /* We load a desktop file from memory to force the app not - * to have an app ID, which would check different codepaths. - */ - fake_desktop_file = g_key_file_new (); - loaded = g_key_file_load_from_data (fake_desktop_file, desktop_file_contents, -1, G_KEY_FILE_NONE, NULL); - g_assert_true (loaded); + /* We load a desktop file from memory to force the app not + * to have an app ID, which would check different codepaths. + */ + fake_desktop_file = g_key_file_new (); + loaded = g_key_file_load_from_data (fake_desktop_file, desktop_file_contents, -1, G_KEY_FILE_NONE, NULL); + g_assert_true (loaded); - appinfo = (GAppInfo*)g_desktop_app_info_new_from_keyfile (fake_desktop_file); - g_assert (appinfo != NULL); + appinfo = (GAppInfo*)g_desktop_app_info_new_from_keyfile (fake_desktop_file); + g_assert (appinfo != NULL); - test_launch_for_app_info (appinfo); + test_launch_for_app_info (appinfo); - g_free (desktop_file_contents); - g_object_unref (appinfo); - g_key_file_unref (fake_desktop_file); - } + g_free (desktop_file_contents); + g_object_unref (appinfo); + g_key_file_unref (fake_desktop_file); + } g_free (exec_line_variants[1]); g_free (exec_line_variants[0]); From d4f804576aeec8c7317dd4adea1864c187b38ecf Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 10:43:24 +0000 Subject: [PATCH 09/32] tests: Use g_assert_*() in mimeapps test rather than g_assert() g_assert() can be compiled out with G_DISABLE_ASSERT, which renders the test useless. The g_assert_*() functions provide more helpful feedback on failure too. Signed-off-by: Philip Withnall --- gio/tests/mimeapps.c | 124 +++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/gio/tests/mimeapps.c b/gio/tests/mimeapps.c index 8a9722fe5..dc593b104 100644 --- a/gio/tests/mimeapps.c +++ b/gio/tests/mimeapps.c @@ -111,7 +111,7 @@ setup (void) gchar *apphome; gchar *mimeapps; gchar *name; - gboolean res; + gint res; GError *error = NULL; dir = g_get_current_dir (); @@ -128,7 +128,7 @@ setup (void) appdir = g_build_filename (xdgdatadir, "applications", NULL); g_test_message ("creating '%s'\n", appdir); res = g_mkdir_with_parents (appdir, 0700); - g_assert (res == 0); + g_assert_cmpint (res, ==, 0); name = g_build_filename (appdir, "mimeapps.list", NULL); g_test_message ("creating '%s'\n", name); @@ -139,7 +139,7 @@ setup (void) apphome = g_build_filename (xdgdatahome, "applications", NULL); g_test_message ("creating '%s'\n", apphome); res = g_mkdir_with_parents (apphome, 0700); - g_assert (res == 0); + g_assert_cmpint (res, ==, 0); name = g_build_filename (apphome, "myapp.desktop", NULL); g_test_message ("creating '%s'\n", name); @@ -214,8 +214,8 @@ test_mime_api (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (def == NULL); - g_assert (list == NULL); + g_assert_null (def); + g_assert_null (list); /* 1. add a non-default association */ g_app_info_add_supports_type (appinfo, contenttype, &error); @@ -223,9 +223,9 @@ test_mime_api (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo)); + g_assert_true (g_app_info_equal (def, appinfo)); g_assert_cmpint (g_list_length (list), ==, 1); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -235,10 +235,10 @@ test_mime_api (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo)); + g_assert_true (g_app_info_equal (def, appinfo)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -248,10 +248,10 @@ test_mime_api (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo)); + g_assert_true (g_app_info_equal (def, appinfo)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -261,10 +261,10 @@ test_mime_api (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo)); + g_assert_true (g_app_info_equal (def, appinfo)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo2)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo2)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -273,8 +273,8 @@ test_mime_api (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (def == NULL); - g_assert (list == NULL); + g_assert_null (def); + g_assert_null (list); g_object_unref (appinfo); g_object_unref (appinfo2); @@ -312,8 +312,8 @@ test_mime_file (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (def == NULL); - g_assert (list == NULL); + g_assert_null (def); + g_assert_null (list); /* 1. add a non-default association */ g_app_info_add_supports_type (appinfo, contenttype, &error); @@ -325,12 +325,12 @@ test_mime_file (void) assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); g_assert_no_error (error); - g_assert (strv_equal (assoc, "myapp.desktop", NULL)); + g_assert_true (strv_equal (assoc, "myapp.desktop", NULL)); g_strfreev (assoc); /* we've unset XDG_DATA_DIRS so there should be no default */ assoc = g_key_file_get_string_list (keyfile, "Default Applications", contenttype, NULL, &error); - g_assert (error != NULL); + g_assert_nonnull (error); g_clear_error (&error); g_key_file_free (keyfile); @@ -345,11 +345,11 @@ test_mime_file (void) assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); g_assert_no_error (error); - g_assert (strv_equal (assoc, "myapp.desktop", "myapp2.desktop", NULL)); + g_assert_true (strv_equal (assoc, "myapp.desktop", "myapp2.desktop", NULL)); g_strfreev (assoc); assoc = g_key_file_get_string_list (keyfile, "Default Applications", contenttype, NULL, &error); - g_assert (error != NULL); + g_assert_nonnull (error); g_clear_error (&error); g_key_file_free (keyfile); @@ -364,7 +364,7 @@ test_mime_file (void) assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); g_assert_no_error (error); - g_assert (strv_equal (assoc, "myapp.desktop", "myapp2.desktop", NULL)); + g_assert_true (strv_equal (assoc, "myapp.desktop", "myapp2.desktop", NULL)); g_strfreev (assoc); str = g_key_file_get_string (keyfile, "Default Applications", contenttype, &error); @@ -384,7 +384,7 @@ test_mime_file (void) assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); g_assert_no_error (error); - g_assert (strv_equal (assoc, "myapp2.desktop", "myapp.desktop", NULL)); + g_assert_true (strv_equal (assoc, "myapp2.desktop", "myapp.desktop", NULL)); g_strfreev (assoc); g_key_file_free (keyfile); @@ -397,10 +397,10 @@ test_mime_file (void) g_assert_no_error (error); res = g_key_file_has_key (keyfile, "Added Associations", contenttype, NULL); - g_assert (!res); + g_assert_false (res); res = g_key_file_has_key (keyfile, "Default Applications", contenttype, NULL); - g_assert (!res); + g_assert_false (res); g_key_file_free (keyfile); @@ -433,9 +433,9 @@ test_mime_default (void) /* myapp3 is set as the default in defaults.list */ def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo3)); + g_assert_true (g_app_info_equal (def, appinfo3)); g_assert_cmpint (g_list_length (list), ==, 1); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo3)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo3)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -445,10 +445,10 @@ test_mime_default (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo3)); /* default is unaffected */ + g_assert_true (g_app_info_equal (def, appinfo3)); /* default is unaffected */ g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo3)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo3)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -458,11 +458,11 @@ test_mime_default (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo3)); + g_assert_true (g_app_info_equal (def, appinfo3)); g_assert_cmpint (g_list_length (list), ==, 3); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->next->data, appinfo3)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->next->data, appinfo3)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -472,11 +472,11 @@ test_mime_default (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo)); + g_assert_true (g_app_info_equal (def, appinfo)); g_assert_cmpint (g_list_length (list), ==, 3); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->next->data, appinfo3)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo2)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->next->data, appinfo3)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -509,10 +509,10 @@ test_mime_default_last_used (void) /* myapp4 and myapp5 can both handle image/bmp */ def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo4)); + g_assert_true (g_app_info_equal (def, appinfo4)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo4)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo5)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo4)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo5)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -522,10 +522,10 @@ test_mime_default_last_used (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo4)); /* default is unaffected */ + g_assert_true (g_app_info_equal (def, appinfo4)); /* default is unaffected */ g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo4)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo5)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo4)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo5)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -535,10 +535,10 @@ test_mime_default_last_used (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo4)); + g_assert_true (g_app_info_equal (def, appinfo4)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo5)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo4)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo5)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo4)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -548,10 +548,10 @@ test_mime_default_last_used (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo5)); + g_assert_true (g_app_info_equal (def, appinfo5)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo5)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo4)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo5)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo4)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -561,10 +561,10 @@ test_mime_default_last_used (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo5)); + g_assert_true (g_app_info_equal (def, appinfo5)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo4)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo5)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo4)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo5)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -574,10 +574,10 @@ test_mime_default_last_used (void) def = g_app_info_get_default_for_type (contenttype, FALSE); list = g_app_info_get_recommended_for_type (contenttype); - g_assert (g_app_info_equal (def, appinfo5)); + g_assert_true (g_app_info_equal (def, appinfo5)); g_assert_cmpint (g_list_length (list), ==, 2); - g_assert (g_app_info_equal ((GAppInfo*)list->data, appinfo5)); - g_assert (g_app_info_equal ((GAppInfo*)list->next->data, appinfo4)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->data, appinfo5)); + g_assert_true (g_app_info_equal ((GAppInfo*)list->next->data, appinfo4)); g_object_unref (def); g_list_free_full (list, g_object_unref); @@ -592,7 +592,7 @@ test_scheme_handler (void) info5 = (GAppInfo*)g_desktop_app_info_new ("myapp5.desktop"); info = g_app_info_get_default_for_uri_scheme ("ftp"); - g_assert (g_app_info_equal (info, info5)); + g_assert_true (g_app_info_equal (info, info5)); g_object_unref (info); g_object_unref (info5); @@ -606,7 +606,7 @@ test_mime_ignore_nonexisting (void) GAppInfo *appinfo; appinfo = (GAppInfo*)g_desktop_app_info_new ("nosuchapp.desktop"); - g_assert (appinfo == NULL); + g_assert_null (appinfo); } static void @@ -617,7 +617,7 @@ test_all (void) all = g_app_info_get_all (); for (l = all; l; l = l->next) - g_assert (G_IS_APP_INFO (l->data)); + g_assert_true (G_IS_APP_INFO (l->data)); g_list_free_full (all, g_object_unref); } From 592365239aa10028f1f22436beb919f22a28f12d Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 10:44:06 +0000 Subject: [PATCH 10/32] tests: Drop extraneous newlines from g_test_message() calls in mimeapps g_test_message() adds a newline already. Signed-off-by: Philip Withnall --- gio/tests/mimeapps.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/gio/tests/mimeapps.c b/gio/tests/mimeapps.c index dc593b104..ab6c30fdb 100644 --- a/gio/tests/mimeapps.c +++ b/gio/tests/mimeapps.c @@ -118,71 +118,71 @@ setup (void) xdgconfighome = g_build_filename (dir, "xdgconfighome", NULL); xdgdatahome = g_build_filename (dir, "xdgdatahome", NULL); xdgdatadir = g_build_filename (dir, "xdgdatadir", NULL); - g_test_message ("setting XDG_CONFIG_HOME to '%s'\n", xdgconfighome); + g_test_message ("setting XDG_CONFIG_HOME to '%s'", xdgconfighome); g_setenv ("XDG_CONFIG_HOME", xdgconfighome, TRUE); - g_test_message ("setting XDG_DATA_HOME to '%s'\n", xdgdatahome); + g_test_message ("setting XDG_DATA_HOME to '%s'", xdgdatahome); g_setenv ("XDG_DATA_HOME", xdgdatahome, TRUE); - g_test_message ("setting XDG_DATA_DIRS to '%s'\n", xdgdatadir); + g_test_message ("setting XDG_DATA_DIRS to '%s'", xdgdatadir); g_setenv ("XDG_DATA_DIRS", xdgdatadir, TRUE); appdir = g_build_filename (xdgdatadir, "applications", NULL); - g_test_message ("creating '%s'\n", appdir); + g_test_message ("creating '%s'", appdir); res = g_mkdir_with_parents (appdir, 0700); g_assert_cmpint (res, ==, 0); name = g_build_filename (appdir, "mimeapps.list", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, defaults_data, -1, &error); g_assert_no_error (error); g_free (name); apphome = g_build_filename (xdgdatahome, "applications", NULL); - g_test_message ("creating '%s'\n", apphome); + g_test_message ("creating '%s'", apphome); res = g_mkdir_with_parents (apphome, 0700); g_assert_cmpint (res, ==, 0); name = g_build_filename (apphome, "myapp.desktop", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, myapp_data, -1, &error); g_assert_no_error (error); g_free (name); name = g_build_filename (apphome, "myapp2.desktop", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, myapp2_data, -1, &error); g_assert_no_error (error); g_free (name); name = g_build_filename (apphome, "myapp3.desktop", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, myapp3_data, -1, &error); g_assert_no_error (error); g_free (name); name = g_build_filename (apphome, "myapp4.desktop", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, myapp4_data, -1, &error); g_assert_no_error (error); g_free (name); name = g_build_filename (apphome, "myapp5.desktop", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, myapp5_data, -1, &error); g_assert_no_error (error); g_free (name); name = g_build_filename (apphome, "nosuchapp.desktop", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, nosuchapp_data, -1, &error); g_assert_no_error (error); g_free (name); mimeapps = g_build_filename (apphome, "mimeapps.list", NULL); - g_test_message ("removing '%s'\n", mimeapps); + g_test_message ("removing '%s'", mimeapps); g_remove (mimeapps); name = g_build_filename (apphome, "mimeinfo.cache", NULL); - g_test_message ("creating '%s'\n", name); + g_test_message ("creating '%s'", name); g_file_set_contents (name, mimecache_data, -1, &error); g_assert_no_error (error); g_free (name); From 2c8ae9f175ef1047de040e344cc2604a7fc3296d Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:25:09 +0000 Subject: [PATCH 11/32] gtestutils: Forbid test paths from containing dots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to make some guarantees in an upcoming commit that test path components won’t clash with file system names used by GLib, add a restriction that test path components cannot start with a dot. This is an API break, but one which anyone is unlikely to have hit. If it is an issue, we can relax the restriction to be a warning. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib/gtestutils.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 72f62af19..29618d733 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -1914,6 +1914,7 @@ g_test_add_vtable (const char *testpath, g_return_if_fail (testpath != NULL); g_return_if_fail (g_path_is_absolute (testpath)); g_return_if_fail (fixture_test_func != NULL); + g_return_if_fail (!test_isolate_dirs || strstr (testpath, "/.") == NULL); suite = g_test_get_root(); segments = g_strsplit (testpath, "/", -1); @@ -2102,6 +2103,10 @@ g_test_set_nonfatal_assertions (void) * the test will be skipped by default, and only run if explicitly * required via the `-p` command-line option or g_test_trap_subprocess(). * + * No component of @testpath may start with a dot (`.`) if the + * %G_TEST_OPTION_ISOLATE_DIRS option is being used; and it is recommended to + * do so even if it isn’t. + * * Since: 2.16 */ void @@ -2140,6 +2145,10 @@ g_test_add_func (const char *testpath, * the test will be skipped by default, and only run if explicitly * required via the `-p` command-line option or g_test_trap_subprocess(). * + * No component of @testpath may start with a dot (`.`) if the + * %G_TEST_OPTION_ISOLATE_DIRS option is being used; and it is recommended to + * do so even if it isn’t. + * * Since: 2.16 */ void From 8da50ac40ca198d3b4e65414fe30988cdaefdadd Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:29:56 +0000 Subject: [PATCH 12/32] gutils: Refactor g_get_home_dir() to use a global variable While this might seem like a regression, it means that the home directory can be overridden by GLib internal code, which will be done in an upcoming commit. This brings g_get_home_dir() inline with functions like g_get_user_data_dir(). Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib/gutils.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/glib/gutils.c b/glib/gutils.c index 3937ff5e5..49b87abfa 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -791,6 +791,9 @@ g_get_real_name (void) return entry->real_name; } +/* Protected by @g_utils_global_lock. */ +static gchar *g_home_dir = NULL; /* (owned) (nullable before initialised) */ + /** * g_get_home_dir: * @@ -820,9 +823,9 @@ g_get_real_name (void) const gchar * g_get_home_dir (void) { - static gchar *home_dir; + G_LOCK (g_utils_global); - if (g_once_init_enter (&home_dir)) + if (g_home_dir == NULL) { gchar *tmp; @@ -899,10 +902,12 @@ g_get_home_dir (void) tmp = "/"; } - g_once_init_leave (&home_dir, tmp); + g_home_dir = g_steal_pointer (&tmp); } - return home_dir; + G_UNLOCK (g_utils_global); + + return g_home_dir; } /** From b87dfb49605bf1e2ef54364ac1d894f8f1a77d3c Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:36:44 +0000 Subject: [PATCH 13/32] =?UTF-8?q?gutils:=20Don=E2=80=99t=20read=20director?= =?UTF-8?q?y=20globals=20outside=20the=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While it is currently OK to read the global variables backing functions like g_get_user_data_dir() without the g_utils_global lock held (since such a read is always preceeded by a critical section where the variable is set to its final value), upcoming changes will allow those variables to be changed. If they are changed from one thread while another thread is calling (for example) g_get_user_data_dir(), the final read from the second thread could race with the first thread. Avoid that by only reading the global variables with the lock held. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- glib/gutils.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/glib/gutils.c b/glib/gutils.c index 49b87abfa..51ea3426b 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -555,13 +555,13 @@ typedef struct gchar *home_dir; } UserDatabaseEntry; +/* These must all be read/written with @g_utils_global held. */ static gchar *g_user_data_dir = NULL; static gchar **g_system_data_dirs = NULL; static gchar *g_user_cache_dir = NULL; static gchar *g_user_config_dir = NULL; static gchar *g_user_runtime_dir = NULL; static gchar **g_system_config_dirs = NULL; - static gchar **g_user_special_dirs = NULL; /* fifteen minutes of fame for everybody */ @@ -823,6 +823,8 @@ static gchar *g_home_dir = NULL; /* (owned) (nullable before initialised) */ const gchar * g_get_home_dir (void) { + const gchar *home_dir; + G_LOCK (g_utils_global); if (g_home_dir == NULL) @@ -905,9 +907,11 @@ g_get_home_dir (void) g_home_dir = g_steal_pointer (&tmp); } + home_dir = g_home_dir; + G_UNLOCK (g_utils_global); - return g_home_dir; + return home_dir; } /** @@ -1223,14 +1227,17 @@ g_build_user_data_dir (void) const gchar * g_get_user_data_dir (void) { + const gchar *user_data_dir; + G_LOCK (g_utils_global); if (g_user_data_dir == NULL) g_user_data_dir = g_build_user_data_dir (); + user_data_dir = g_user_data_dir; G_UNLOCK (g_utils_global); - return g_user_data_dir; + return user_data_dir; } static gchar * @@ -1283,14 +1290,17 @@ g_build_user_config_dir (void) const gchar * g_get_user_config_dir (void) { + const gchar *user_config_dir; + G_LOCK (g_utils_global); if (g_user_config_dir == NULL) g_user_config_dir = g_build_user_config_dir (); + user_config_dir = g_user_config_dir; G_UNLOCK (g_utils_global); - return g_user_config_dir; + return user_config_dir; } static gchar * @@ -1342,14 +1352,17 @@ g_build_user_cache_dir (void) const gchar * g_get_user_cache_dir (void) { + const gchar *user_cache_dir; + G_LOCK (g_utils_global); if (g_user_cache_dir == NULL) g_user_cache_dir = g_build_user_cache_dir (); + user_cache_dir = g_user_cache_dir; G_UNLOCK (g_utils_global); - return g_user_cache_dir; + return user_cache_dir; } static gchar * @@ -1402,14 +1415,17 @@ g_build_user_runtime_dir (void) const gchar * g_get_user_runtime_dir (void) { + const gchar *user_runtime_dir; + G_LOCK (g_utils_global); if (g_user_runtime_dir == NULL) g_user_runtime_dir = g_build_user_runtime_dir (); + user_runtime_dir = g_user_runtime_dir; G_UNLOCK (g_utils_global); - return g_user_runtime_dir; + return user_runtime_dir; } #ifdef HAVE_CARBON @@ -1774,6 +1790,8 @@ g_reload_user_special_dirs_cache (void) const gchar * g_get_user_special_dir (GUserDirectory directory) { + const gchar *user_special_dir; + g_return_val_if_fail (directory >= G_USER_DIRECTORY_DESKTOP && directory < G_USER_N_DIRECTORIES, NULL); @@ -1789,10 +1807,11 @@ g_get_user_special_dir (GUserDirectory directory) if (g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] == NULL) g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = g_build_filename (g_get_home_dir (), "Desktop", NULL); } + user_special_dir = g_user_special_dirs[directory]; G_UNLOCK (g_utils_global); - return g_user_special_dirs[directory]; + return user_special_dir; } #ifdef G_OS_WIN32 @@ -2066,14 +2085,17 @@ g_build_system_data_dirs (void) const gchar * const * g_get_system_data_dirs (void) { + const gchar * const *system_data_dirs; + G_LOCK (g_utils_global); if (g_system_data_dirs == NULL) g_system_data_dirs = g_build_system_data_dirs (); + system_data_dirs = (const gchar * const *) g_system_data_dirs; G_UNLOCK (g_utils_global); - return (const gchar * const *) g_system_data_dirs; + return system_data_dirs; } static gchar ** @@ -2138,14 +2160,17 @@ g_build_system_config_dirs (void) const gchar * const * g_get_system_config_dirs (void) { + const gchar * const *system_config_dirs; + G_LOCK (g_utils_global); if (g_system_config_dirs == NULL) g_system_config_dirs = g_build_system_config_dirs (); + system_config_dirs = (const gchar * const *) g_system_config_dirs; G_UNLOCK (g_utils_global); - return (const gchar * const *) g_system_config_dirs; + return system_config_dirs; } /** From 91defdb34eb71ff61d1d7fb2a518eda7d1824e42 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:47:18 +0000 Subject: [PATCH 14/32] gutils: Add internal API to override XDG directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new internal function, g_set_user_dirs(), which will safely override the values returned by g_get_user_data_dir() and friends, and the value returned by g_get_home_dir(). This is intended to be used by unit tests, and will be hooked up to them in a following commit. This can be called as many times as needed by the current process. It’s thread-safe. It does not modify the environment, so none of the changes are propagated to any subsequently spawned subprocesses. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- docs/reference/glib/Makefile.am | 1 + docs/reference/glib/meson.build | 1 + glib.supp | 20 ++++++ glib/Makefile.am | 1 + glib/gutils.c | 108 ++++++++++++++++++++++++++++++++ glib/gutilsprivate.h | 33 ++++++++++ glib/meson.build | 1 + 7 files changed, 165 insertions(+) create mode 100644 glib/gutilsprivate.h diff --git a/docs/reference/glib/Makefile.am b/docs/reference/glib/Makefile.am index 4d4c8d80c..a3e716867 100644 --- a/docs/reference/glib/Makefile.am +++ b/docs/reference/glib/Makefile.am @@ -64,6 +64,7 @@ IGNORE_HFILES = \ glib-init.h \ gconstructor.h \ valgrind.h \ + gutilsprivate.h \ gvalgrind.h \ $(NULL) diff --git a/docs/reference/glib/meson.build b/docs/reference/glib/meson.build index eca891a20..2fb82481f 100644 --- a/docs/reference/glib/meson.build +++ b/docs/reference/glib/meson.build @@ -36,6 +36,7 @@ if get_option('gtk_doc') 'glib-init.h', 'gconstructor.h', 'valgrind.h', + 'gutilsprivate.h', 'gvalgrind.h', ] diff --git a/glib.supp b/glib.supp index c8f3682ef..3ff1fdbc0 100644 --- a/glib.supp +++ b/glib.supp @@ -544,6 +544,26 @@ fun:g_object_new_valist } +# g_set_user_dirs() deliberately leaks the previous cached g_get_user_*() values. +{ + g_set_user_dirs_str + Memcheck:Leak + fun:malloc + ... + fun:set_str_if_different + fun:g_set_user_dirs +} + +# g_set_user_dirs() deliberately leaks the previous cached g_get_user_*() values. +{ + g_set_user_dirs_strv + Memcheck:Leak + fun:malloc + ... + fun:set_strv_if_different + fun:g_set_user_dirs +} + # g_get_system_data_dirs() caches a one-time allocation { g_get_system_data_dirs diff --git a/glib/Makefile.am b/glib/Makefile.am index e072b8829..e433d48f3 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -191,6 +191,7 @@ libglib_2_0_la_SOURCES = \ gunicodeprivate.h \ gurifuncs.c \ gutils.c \ + gutilsprivate.h \ guuid.c \ gvalgrind.h \ gvariant.h \ diff --git a/glib/gutils.c b/glib/gutils.c index 51ea3426b..1e267602a 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -29,6 +29,7 @@ #include "config.h" #include "gutils.h" +#include "gutilsprivate.h" #include #include @@ -1177,6 +1178,113 @@ g_set_application_name (const gchar *application_name) g_warning ("g_set_application_name() called multiple times"); } +/* Set @global_str to a copy of @new_value if it’s currently unset or has a + * different value. If its current value matches @new_value, do nothing. If + * replaced, we have to leak the old value as client code could still have + * pointers to it. */ +static void +set_str_if_different (gchar **global_str, + const gchar *type, + const gchar *new_value) +{ + if (*global_str == NULL || + !g_str_equal (new_value, *global_str)) + { + g_debug ("g_set_user_dirs: Setting %s to %s", type, new_value); + + /* We have to leak the old value, as user code could be retaining pointers + * to it. */ + *global_str = g_strdup (new_value); + } +} + +static void +set_strv_if_different (gchar ***global_strv, + const gchar *type, + const gchar * const *new_value) +{ + if (*global_strv == NULL || + !g_strv_equal (new_value, (const gchar * const *) *global_strv)) + { + gchar *new_value_str = g_strjoinv (":", (gchar **) new_value); + g_debug ("g_set_user_dirs: Setting %s to %s", type, new_value_str); + g_free (new_value_str); + + /* We have to leak the old value, as user code could be retaining pointers + * to it. */ + *global_strv = g_strdupv ((gchar **) new_value); + } +} + +/* + * g_set_user_dirs: + * @first_dir_type: Type of the first directory to set + * @...: Value to set the first directory to, followed by additional type/value + * pairs, followed by %NULL + * + * Set one or more ‘user’ directories to custom values. This is intended to be + * used by test code (particularly with the %G_TEST_OPTION_ISOLATE_DIRS option) + * to override the values returned by the following functions, so that test + * code can be run without touching an installed system and user data: + * + * - g_get_home_dir() — use type `HOME`, pass a string + * - g_get_user_cache_dir() — use type `XDG_CACHE_HOME`, pass a string + * - g_get_system_config_dirs() — use type `XDG_CONFIG_DIRS`, pass a + * %NULL-terminated string array + * - g_get_user_config_dir() — use type `XDG_CONFIG_HOME`, pass a string + * - g_get_system_data_dirs() — use type `XDG_DATA_DIRS`, pass a + * %NULL-terminated string array + * - g_get_user_data_dir() — use type `XDG_DATA_HOME`, pass a string + * - g_get_user_runtime_dir() — use type `XDG_RUNTIME_DIR`, pass a string + * + * The list must be terminated with a %NULL type. All of the values must be + * non-%NULL — passing %NULL as a value won’t reset a directory. If a reference + * to a directory from the calling environment needs to be kept, copy it before + * the first call to g_set_user_dirs(). g_set_user_dirs() can be called multiple + * times. + * + * Since: 2.60 + */ +/*< private > */ +void +g_set_user_dirs (const gchar *first_dir_type, + ...) +{ + va_list args; + const gchar *dir_type; + + G_LOCK (g_utils_global); + + va_start (args, first_dir_type); + + for (dir_type = first_dir_type; dir_type != NULL; dir_type = va_arg (args, const gchar *)) + { + gconstpointer dir_value = va_arg (args, gconstpointer); + g_assert (dir_value != NULL); + + if (g_str_equal (dir_type, "HOME")) + set_str_if_different (&g_home_dir, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_CACHE_HOME")) + set_str_if_different (&g_user_cache_dir, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_CONFIG_DIRS")) + set_strv_if_different (&g_system_config_dirs, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_CONFIG_HOME")) + set_str_if_different (&g_user_config_dir, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_DATA_DIRS")) + set_strv_if_different (&g_system_data_dirs, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_DATA_HOME")) + set_str_if_different (&g_user_data_dir, dir_type, dir_value); + else if (g_str_equal (dir_type, "XDG_RUNTIME_DIR")) + set_str_if_different (&g_user_runtime_dir, dir_type, dir_value); + else + g_assert_not_reached (); + } + + va_end (args); + + G_UNLOCK (g_utils_global); +} + static gchar * g_build_user_data_dir (void) { diff --git a/glib/gutilsprivate.h b/glib/gutilsprivate.h new file mode 100644 index 000000000..5a0686086 --- /dev/null +++ b/glib/gutilsprivate.h @@ -0,0 +1,33 @@ +/* + * Copyright © 2018 Endless Mobile, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * Author: Philip Withnall + */ + +#ifndef __G_UTILS_PRIVATE_H__ +#define __G_UTILS_PRIVATE_H__ + +#include "gtypes.h" + +G_BEGIN_DECLS + +GLIB_AVAILABLE_IN_2_60 +void g_set_user_dirs (const gchar *first_dir_type, + ...) G_GNUC_NULL_TERMINATED; + +G_END_DECLS + +#endif /* __G_UTILS_PRIVATE_H__ */ diff --git a/glib/meson.build b/glib/meson.build index 3275ee6e1..1fe4a6e73 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -199,6 +199,7 @@ glib_sources = files( 'gunidecomp.c', 'gurifuncs.c', 'gutils.c', + 'gutilsprivate.h', 'guuid.c', 'gvariant.c', 'gvariant-core.c', From 13730c27c04dfff23b84e3c4160dec19d9598f13 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:50:22 +0000 Subject: [PATCH 15/32] gtestutils: Add XDG directory isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new G_TEST_OPTIONS_ISOLATE_XDG_DIRS option for g_test_init() which automatically creates a temporary set of XDG directories, and a temporary home directory, and overrides the g_get_user_data_dir() (etc.) functions for the duration of the unit test with the temporary values. This is intended to better isolate unit tests from the user’s actual data and home directory. It works with g_test_subprocess(), but does not work with subprocesses spawned manually by the test — each unit test’s code will need to be amended to correctly set the XDG_* environment variables in the environment of any spawned subprocess. “Why not solve that by setting the XDG environment variables for the whole unit test process tree?” I hear you say. Setting environment variables is not thread safe and they would need to be re-set for each unit test, once worker threads have potentially been spawned. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/538 --- docs/reference/glib/glib-sections.txt | 1 + glib/gtestutils.c | 272 ++++++++++++++++++++++++-- glib/gtestutils.h | 30 +++ 3 files changed, 282 insertions(+), 21 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 2d3c7f65a..6b4863dcc 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -3102,6 +3102,7 @@ g_compute_hmac_for_bytes
Testing testing +G_TEST_OPTION_ISOLATE_DIRS g_test_minimized_result g_test_maximized_result g_test_init diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 29618d733..9252ebbf5 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -27,7 +27,6 @@ #include #include #include -#include #endif #include #include @@ -44,6 +43,7 @@ #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ +#include #include "gmain.h" #include "gpattern.h" @@ -53,6 +53,7 @@ #include "gslice.h" #include "gspawn.h" #include "glib-private.h" +#include "gutilsprivate.h" /** @@ -825,6 +826,9 @@ static const char * const g_test_result_names[] = { static int test_log_fd = -1; static gboolean test_mode_fatal = TRUE; static gboolean g_test_run_once = TRUE; +static gboolean test_isolate_dirs = FALSE; +static gchar *test_isolate_dirs_tmpdir = NULL; +static const gchar *test_tmpdir = NULL; static gboolean test_run_list = FALSE; static gchar *test_run_seedstr = NULL; G_LOCK_DEFINE_STATIC (test_run_rand); @@ -1261,15 +1265,139 @@ parse_args (gint *argc_p, *argc_p = e; } +/* A fairly naive `rm -rf` implementation to clean up after unit tests. */ +static void +rm_rf (const gchar *path) +{ + GDir *dir = NULL; + const gchar *entry; + + dir = g_dir_open (path, 0, NULL); + if (dir == NULL) + { + /* Assume it’s a file. */ + g_remove (path); + return; + } + + while ((entry = g_dir_read_name (dir)) != NULL) + { + gchar *sub_path = g_build_filename (path, entry, NULL); + rm_rf (sub_path); + g_free (sub_path); + } + + g_dir_close (dir); + + g_rmdir (path); +} + +/* Implement the %G_TEST_OPTION_ISOLATE_DIRS option, iff it’s enabled. Create + * a temporary directory for this unit test (disambiguated using @test_run_name) + * and use g_set_user_dirs() to point various XDG directories into it, without + * having to call setenv() in a process which potentially has threads running. + * + * Note that this is called for each unit test, and hence won’t have taken + * effect before g_test_run() is called in the unit test’s main(). Hence + * references to XDG variables in main() will not be using the temporary + * directory. */ +static gboolean +test_do_isolate_dirs (GError **error) +{ + gchar *subdir = NULL; + gchar *home_dir = NULL, *cache_dir = NULL, *config_dir = NULL; + gchar *data_dir = NULL, *runtime_dir = NULL; + gchar *config_dirs[3]; + gchar *data_dirs[3]; + + if (!test_isolate_dirs) + return TRUE; + + /* The @test_run_name includes the test suites, so may be several directories + * deep. Add a `.dirs` directory to contain all the paths we create, and + * guarantee none of them clash with test paths below the current one — test + * paths may not contain components starting with `.`. */ + subdir = g_build_filename (test_tmpdir, test_run_name, ".dirs", NULL); + + /* We have to create the runtime directory (because it must be bound to + * the session lifetime, which we consider to be the lifetime of the unit + * test for testing purposes — see + * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html. + * We don’t need to create the other directories — the specification + * requires that client code create them if they don’t exist. Not creating + * them automatically is a good test of clients’ adherence to the spec + * and error handling of missing directories. */ + runtime_dir = g_build_filename (subdir, "runtime", NULL); + if (g_mkdir_with_parents (runtime_dir, 0700) != 0) + { + gint saved_errno = errno; + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + "Failed to create XDG_RUNTIME_DIR ‘%s’: %s", + runtime_dir, g_strerror (saved_errno)); + g_free (runtime_dir); + g_free (subdir); + return FALSE; + } + + home_dir = g_build_filename (subdir, "home", NULL); + cache_dir = g_build_filename (subdir, "cache", NULL); + config_dir = g_build_filename (subdir, "config", NULL); + data_dir = g_build_filename (subdir, "data", NULL); + + config_dirs[0] = g_build_filename (subdir, "system-config1", NULL); + config_dirs[1] = g_build_filename (subdir, "system-config2", NULL); + config_dirs[2] = NULL; + + data_dirs[0] = g_build_filename (subdir, "system-data1", NULL); + data_dirs[1] = g_build_filename (subdir, "system-data2", NULL); + data_dirs[2] = NULL; + + /* Remember to update the documentation for %G_TEST_OPTION_ISOLATE_DIRS if + * this list changes. */ + g_set_user_dirs ("HOME", home_dir, + "XDG_CACHE_HOME", cache_dir, + "XDG_CONFIG_DIRS", config_dirs, + "XDG_CONFIG_HOME", config_dir, + "XDG_DATA_DIRS", data_dirs, + "XDG_DATA_HOME", data_dir, + "XDG_RUNTIME_DIR", runtime_dir, + NULL); + + g_free (runtime_dir); + g_free (data_dir); + g_free (config_dir); + g_free (cache_dir); + g_free (home_dir); + g_free (data_dirs[1]); + g_free (data_dirs[0]); + g_free (config_dirs[1]); + g_free (config_dirs[0]); + g_free (subdir); + + return TRUE; +} + +/* Clean up after test_do_isolate_dirs(). */ +static void +test_rm_isolate_dirs (void) +{ + gchar *subdir = NULL; + + if (!test_isolate_dirs) + return; + + subdir = g_build_filename (test_tmpdir, test_run_name, NULL); + rm_rf (subdir); + g_free (subdir); +} + /** * g_test_init: * @argc: Address of the @argc parameter of the main() function. * Changed if any arguments were handled. * @argv: Address of the @argv parameter of main(). * Any parameters understood by g_test_init() stripped before return. - * @...: %NULL-terminated list of special options. Currently the only - * defined option is `"no_g_set_prgname"`, which - * will cause g_test_init() to not call g_set_prgname(). + * @...: %NULL-terminated list of special options, documented below. * * Initialize the GLib testing framework, e.g. by seeding the * test random number generator, the name for g_get_prgname() @@ -1303,6 +1431,14 @@ parse_args (gint *argc_p, * * - `--debug-log`: Debug test logging output. * + * Options which can be passed to @... are: + * + * - `"no_g_set_prgname"`: Causes g_test_init() to not call g_set_prgname(). + * - %G_TEST_OPTION_ISOLATE_DIRS: Creates a unique temporary directory for each + * unit test and uses g_set_user_dirs() to set XDG directories to point into + * that temporary directory for the duration of the unit test. See the + * documentation for %G_TEST_OPTION_ISOLATE_DIRS. + * * Since 2.58, if tests are compiled with `G_DISABLE_ASSERT` defined, * g_test_init() will print an error and exit. This is to prevent no-op tests * from being executed, as g_assert() is commonly (erroneously) used in unit @@ -1335,6 +1471,8 @@ void { if (g_strcmp0 (option, "no_g_set_prgname") == 0) no_g_set_prgname = TRUE; + else if (g_strcmp0 (option, G_TEST_OPTION_ISOLATE_DIRS) == 0) + test_isolate_dirs = TRUE; } va_end (args); @@ -1348,6 +1486,77 @@ void if (!g_get_prgname() && !no_g_set_prgname) g_set_prgname ((*argv)[0]); + /* Set up the temporary directory for isolating the test. We have to do this + * early, as we want the return values from g_get_user_data_dir() (and + * friends) to return subdirectories of the temporary directory throughout + * the setup function, test, and teardown function, for each unit test. + * See test_do_isolate_dirs(). + * + * The directory is deleted at the bottom of g_test_run(). + * + * Rather than setting the XDG_* environment variables we use a new + * G_TEST_TMPDIR variable which gives the top-level temporary directory. This + * allows test subprocesses to reuse the same temporary directory when + * g_test_init() is called in them. */ + if (test_isolate_dirs) + { + if (g_getenv ("G_TEST_TMPDIR") == NULL) + { + gchar *test_prgname = NULL; + gchar *tmpl = NULL; + GError *local_error = NULL; + + test_prgname = g_path_get_basename (g_get_prgname ()); + if (*test_prgname == '\0') + test_prgname = g_strdup ("unknown"); + tmpl = g_strdup_printf ("test_%s_XXXXXX", test_prgname); + g_free (test_prgname); + + test_isolate_dirs_tmpdir = g_dir_make_tmp (tmpl, &local_error); + if (local_error != NULL) + { + g_printerr ("%s: Failed to create temporary directory: %s\n", + (*argv)[0], local_error->message); + g_error_free (local_error); + exit (1); + } + g_free (tmpl); + + /* Propagate the temporary directory to subprocesses. */ + g_setenv ("G_TEST_TMPDIR", test_isolate_dirs_tmpdir, TRUE); + + /* And clear the traditional environment variables so subprocesses + * spawned by the code under test can’t trash anything. If a test + * spawns a process, the test is responsible for propagating + * appropriate environment variables. + * + * We assume that any in-process code will use g_get_user_data_dir() + * and friends, rather than getenv() directly. + * + * We set them to ‘/dev/null’ as that should fairly obviously not + * accidentally work, and should be fairly greppable. */ + { + const gchar *overridden_environment_variables[] = + { + "HOME", + "XDG_CACHE_HOME", + "XDG_CONFIG_DIRS", + "XDG_CONFIG_HOME", + "XDG_DATA_DIRS", + "XDG_DATA_HOME", + "XDG_RUNTIME_DIR", + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS (overridden_environment_variables); i++) + g_setenv (overridden_environment_variables[i], "/dev/null", TRUE); + } + } + + /* Cache this for the remainder of this process’ lifetime. */ + test_tmpdir = g_getenv ("G_TEST_TMPDIR"); + } + /* sanity check */ if (test_tap_log) { @@ -1798,6 +2007,14 @@ g_test_run (void) if (g_test_run_suite (g_test_get_root()) != 0) return 1; + /* Clean up the temporary directory. */ + if (test_isolate_dirs_tmpdir != NULL) + { + rm_rf (test_isolate_dirs_tmpdir); + g_free (test_isolate_dirs_tmpdir); + test_isolate_dirs_tmpdir = NULL; + } + /* 77 is special to Automake's default driver, but not Automake's TAP driver * or Perl's prove(1) TAP driver. */ if (test_tap_log) @@ -2362,25 +2579,38 @@ test_case_run (GTestCase *tc) g_test_skip ("by request (-s option)"); else { - g_timer_start (test_run_timer); - fixture = tc->fixture_size ? g_malloc0 (tc->fixture_size) : tc->test_data; - test_run_seed (test_run_seedstr); - if (tc->fixture_setup) - tc->fixture_setup (fixture, tc->test_data); - tc->fixture_test (fixture, tc->test_data); - test_trap_clear(); - while (test_destroy_queue) + GError *local_error = NULL; + + if (!test_do_isolate_dirs (&local_error)) { - DestroyEntry *dentry = test_destroy_queue; - test_destroy_queue = dentry->next; - dentry->destroy_func (dentry->destroy_data); - g_slice_free (DestroyEntry, dentry); + g_test_log (G_TEST_LOG_ERROR, local_error->message, NULL, 0, NULL); + g_test_fail (); + g_error_free (local_error); } - if (tc->fixture_teardown) - tc->fixture_teardown (fixture, tc->test_data); - if (tc->fixture_size) - g_free (fixture); - g_timer_stop (test_run_timer); + else + { + g_timer_start (test_run_timer); + fixture = tc->fixture_size ? g_malloc0 (tc->fixture_size) : tc->test_data; + test_run_seed (test_run_seedstr); + if (tc->fixture_setup) + tc->fixture_setup (fixture, tc->test_data); + tc->fixture_test (fixture, tc->test_data); + test_trap_clear(); + while (test_destroy_queue) + { + DestroyEntry *dentry = test_destroy_queue; + test_destroy_queue = dentry->next; + dentry->destroy_func (dentry->destroy_data); + g_slice_free (DestroyEntry, dentry); + } + if (tc->fixture_teardown) + tc->fixture_teardown (fixture, tc->test_data); + if (tc->fixture_size) + g_free (fixture); + g_timer_stop (test_run_timer); + } + + test_rm_isolate_dirs (); } success = test_run_success; test_run_success = G_TEST_RUN_FAILURE; diff --git a/glib/gtestutils.h b/glib/gtestutils.h index 3028514fb..4aa6fa168 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -165,6 +165,36 @@ void g_test_init (int *argc, char ***argv, ...) G_GNUC_NULL_TERMINATED; +/** + * G_TEST_OPTION_ISOLATE_DIRS: + * + * Creates a unique temporary directory for each unit test and uses + * g_set_user_dirs() to set XDG directories to point into subdirectories of it + * for the duration of the unit test. The directory tree is cleaned up after the + * test finishes successfully. Note that this doesn’t take effect until + * g_test_run() is called, so calls to (for example) g_get_user_home_dir() will + * return the system-wide value when made in a test program’s main() function. + * + * The following functions will return subdirectories of the temporary directory + * when this option is used. The specific subdirectory paths in use are not + * guaranteed to be stable API — always use a getter function to retrieve them. + * + * - g_get_home_dir() + * - g_get_user_cache_dir() + * - g_get_system_config_dirs() + * - g_get_user_config_dir() + * - g_get_system_data_dirs() + * - g_get_user_data_dir() + * - g_get_user_runtime_dir() + * + * The subdirectories may not be created by the test harness; as with normal + * calls to functions like g_get_user_cache_dir(), the caller must be prepared + * to create the directory if it doesn’t exist. + * + * Since: 2.60 + */ +#define G_TEST_OPTION_ISOLATE_DIRS "isolate_dirs" + /* While we discourage its use, g_assert() is often used in unit tests * (especially in legacy code). g_assert_*() should really be used instead. * g_assert() can be disabled at client program compile time, which can render From 528d537da8104a7d71e68ee5c405520bb6b2c2cd Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 3 Dec 2018 14:50:30 +0000 Subject: [PATCH 16/32] tests: Port desktop-app-info to use g_assert_*() g_assert() is for runtime code, and can be compiled out. g_assert_*() cannot be compiled out, and give more helpful failure messages for specific types. Signed-off-by: Philip Withnall --- gio/tests/desktop-app-info.c | 100 +++++++++++++++++------------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index 48317967c..176e3961f 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -40,13 +40,13 @@ create_app_info (const char *name) name, G_APP_INFO_CREATE_NONE, &error); - g_assert (error == NULL); + g_assert_no_error (error); /* this is necessary to ensure that the info is saved */ g_app_info_set_as_default_for_type (info, "application/x-blah", &error); - g_assert (error == NULL); + g_assert_no_error (error); g_app_info_remove_supports_type (info, "application/x-blah", &error); - g_assert (error == NULL); + g_assert_no_error (error); g_app_info_reset_type_associations ("application/x-blah"); return info; @@ -64,34 +64,34 @@ test_delete (void) info = create_app_info ("Blah"); id = g_app_info_get_id (info); - g_assert (id != NULL); + g_assert_nonnull (id); filename = g_build_filename (basedir, "applications", id, NULL); res = g_file_test (filename, G_FILE_TEST_EXISTS); - g_assert (res); + g_assert_true (res); res = g_app_info_can_delete (info); - g_assert (res); + g_assert_true (res); res = g_app_info_delete (info); - g_assert (res); + g_assert_true (res); res = g_file_test (filename, G_FILE_TEST_EXISTS); - g_assert (!res); + g_assert_false (res); g_object_unref (info); if (g_file_test ("/usr/share/applications/gedit.desktop", G_FILE_TEST_EXISTS)) { info = (GAppInfo*)g_desktop_app_info_new_from_filename ("/usr/share/applications/gedit.desktop"); - g_assert (info); + g_assert_nonnull (info); res = g_app_info_can_delete (info); - g_assert (!res); + g_assert_false (res); res = g_app_info_delete (info); - g_assert (!res); + g_assert_false (res); } g_free (filename); @@ -109,33 +109,33 @@ test_default (void) info3 = create_app_info ("Blah3"); g_app_info_set_as_default_for_type (info1, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); g_app_info_set_as_default_for_type (info2, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); info = g_app_info_get_default_for_type ("application/x-test", FALSE); - g_assert (info != NULL); + g_assert_nonnull (info); g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); g_object_unref (info); /* now try adding something, but not setting as default */ g_app_info_add_supports_type (info3, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); /* check that info2 is still default */ info = g_app_info_get_default_for_type ("application/x-test", FALSE); - g_assert (info != NULL); + g_assert_nonnull (info); g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); g_object_unref (info); /* now remove info1 again */ g_app_info_remove_supports_type (info1, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); /* and make sure info2 is still default */ info = g_app_info_get_default_for_type ("application/x-test", FALSE); - g_assert (info != NULL); + g_assert_nonnull (info); g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); g_object_unref (info); @@ -143,7 +143,7 @@ test_default (void) g_app_info_reset_type_associations ("application/x-test"); list = g_app_info_get_all_for_type ("application/x-test"); - g_assert (list == NULL); + g_assert_null (list); g_app_info_delete (info1); g_app_info_delete (info2); @@ -165,17 +165,17 @@ test_fallback (void) info1 = create_app_info ("Test1"); info2 = create_app_info ("Test2"); - g_assert (g_content_type_is_a ("text/x-python", "text/plain")); + g_assert_true (g_content_type_is_a ("text/x-python", "text/plain")); apps = g_app_info_get_all_for_type ("text/x-python"); old_length = g_list_length (apps); g_list_free_full (apps, g_object_unref); g_app_info_add_supports_type (info1, "text/x-python", &error); - g_assert (error == NULL); + g_assert_no_error (error); g_app_info_add_supports_type (info2, "text/plain", &error); - g_assert (error == NULL); + g_assert_no_error (error); /* check that both apps are registered */ apps = g_app_info_get_all_for_type ("text/x-python"); @@ -183,18 +183,18 @@ test_fallback (void) /* check that Test1 is among the recommended apps */ recomm = g_app_info_get_recommended_for_type ("text/x-python"); - g_assert (recomm != NULL); + g_assert_nonnull (recomm); for (l = recomm; l; l = l->next) { app = l->data; if (g_app_info_equal (info1, app)) break; } - g_assert (g_app_info_equal (info1, app)); + g_assert_true (g_app_info_equal (info1, app)); /* and that Test2 is among the fallback apps */ fallback = g_app_info_get_fallback_for_type ("text/x-python"); - g_assert (fallback != NULL); + g_assert_nonnull (fallback); for (l = fallback; l; l = l->next) { app = l->data; @@ -205,11 +205,11 @@ test_fallback (void) /* check that recomm + fallback = all applications */ list = g_list_concat (g_list_copy (recomm), g_list_copy (fallback)); - g_assert (g_list_length (list) == g_list_length (apps)); + g_assert_cmpuint (g_list_length (list), ==, g_list_length (apps)); for (l = list, m = apps; l != NULL && m != NULL; l = l->next, m = m->next) { - g_assert (g_app_info_equal (l->data, m->data)); + g_assert_true (g_app_info_equal (l->data, m->data)); } g_list_free (list); @@ -239,32 +239,32 @@ test_last_used (void) info2 = create_app_info ("Test2"); g_app_info_set_as_default_for_type (info1, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); g_app_info_add_supports_type (info2, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); applications = g_app_info_get_recommended_for_type ("application/x-test"); - g_assert (g_list_length (applications) == 2); + g_assert_cmpuint (g_list_length (applications), ==, 2); /* the first should be the default app now */ - g_assert (g_app_info_equal (g_list_nth_data (applications, 0), info1)); - g_assert (g_app_info_equal (g_list_nth_data (applications, 1), info2)); + g_assert_true (g_app_info_equal (g_list_nth_data (applications, 0), info1)); + g_assert_true (g_app_info_equal (g_list_nth_data (applications, 1), info2)); g_list_free_full (applications, g_object_unref); g_app_info_set_as_last_used_for_type (info2, "application/x-test", &error); - g_assert (error == NULL); + g_assert_no_error (error); applications = g_app_info_get_recommended_for_type ("application/x-test"); - g_assert (g_list_length (applications) == 2); + g_assert_cmpuint (g_list_length (applications), ==, 2); default_app = g_app_info_get_default_for_type ("application/x-test", FALSE); - g_assert (g_app_info_equal (default_app, info1)); + g_assert_true (g_app_info_equal (default_app, info1)); /* the first should be the other app now */ - g_assert (g_app_info_equal (g_list_nth_data (applications, 0), info2)); - g_assert (g_app_info_equal (g_list_nth_data (applications, 1), info1)); + g_assert_true (g_app_info_equal (g_list_nth_data (applications, 0), info2)); + g_assert_true (g_app_info_equal (g_list_nth_data (applications, 1), info1)); g_list_free_full (applications, g_object_unref); @@ -366,10 +366,10 @@ test_extra_getters (void) setlocale (LC_ALL, ""); appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL)); - g_assert (appinfo != NULL); + g_assert_nonnull (appinfo); - g_assert (g_desktop_app_info_has_key (appinfo, "Terminal")); - g_assert (!g_desktop_app_info_has_key (appinfo, "Bratwurst")); + g_assert_true (g_desktop_app_info_has_key (appinfo, "Terminal")); + g_assert_false (g_desktop_app_info_has_key (appinfo, "Bratwurst")); s = g_desktop_app_info_get_string (appinfo, "StartupWMClass"); g_assert_cmpstr (s, ==, "appinfo-class"); @@ -387,7 +387,7 @@ test_extra_getters (void) g_free (s); b = g_desktop_app_info_get_boolean (appinfo, "Terminal"); - g_assert (b); + g_assert_true (b); g_object_unref (appinfo); @@ -400,7 +400,7 @@ wait_for_file (const gchar *want_this, const gchar *but_not_this, const gchar *or_this) { - gint retries = 600; + guint retries = 600; /* I hate time-based conditions in tests, but this will wait up to one * whole minute for "touch file" to finish running. I think it should @@ -411,12 +411,12 @@ wait_for_file (const gchar *want_this, while (access (want_this, F_OK) != 0) { g_usleep (100000); /* 100ms */ - g_assert (retries); + g_assert_cmpuint (retries, >, 0); retries--; } - g_assert (access (but_not_this, F_OK) != 0); - g_assert (access (or_this, F_OK) != 0); + g_assert_cmpuint (access (but_not_this, F_OK), !=, 0); + g_assert_cmpuint (access (or_this, F_OK), !=, 0); unlink (want_this); unlink (but_not_this); @@ -431,7 +431,7 @@ test_actions (void) gchar *name; appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "appinfo-test-actions.desktop", NULL)); - g_assert (appinfo != NULL); + g_assert_nonnull (appinfo); actions = g_desktop_app_info_list_actions (appinfo); g_assert_cmpstr (actions[0], ==, "frob"); @@ -453,8 +453,8 @@ test_actions (void) g_free (name); name = g_desktop_app_info_get_action_name (appinfo, "broken"); - g_assert (name != NULL); - g_assert (g_utf8_validate (name, -1, NULL)); + g_assert_nonnull (name); + g_assert_true (g_utf8_validate (name, -1, NULL)); g_free (name); unlink ("frob"); unlink ("tweak"); unlink ("twiddle"); @@ -528,8 +528,8 @@ run_apps (const gchar *command, envp = g_environ_unsetenv (envp, "XDG_CURRENT_DESKTOP"); success = g_spawn_sync (NULL, argv, envp, 0, NULL, NULL, &out, NULL, &status, NULL); - g_assert (success); - g_assert (status == 0); + g_assert_true (success); + g_assert_cmpuint (status, ==, 0); g_strfreev (envp); g_strfreev (argv); From b76372685dff9f3fbe896194c4a469c88b562ba5 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 13:51:35 +0000 Subject: [PATCH 17/32] gutils: Split out building of g_get_home_dir() path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we can have problems calling g_get_home_dir() from within a g_build_*_dir() function elsewhere in gutils.c: • There will be a deadlock due to trying to recursively acquire the g_utils_global lock. • A stale g_home_dir value may be used if a test harness has called g_set_user_dirs() in the interim. Fix that by splitting the code to find/construct the home path out of g_get_home_dir() into g_build_home_dir(), the same way it’s split for the other g_get_*() functions. Call g_build_home_dir() from any call site where the g_utils_global lock is held. Signed-off-by: Philip Withnall --- glib/gutils.c | 178 ++++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/glib/gutils.c b/glib/gutils.c index 1e267602a..2fcccc33d 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -795,6 +795,76 @@ g_get_real_name (void) /* Protected by @g_utils_global_lock. */ static gchar *g_home_dir = NULL; /* (owned) (nullable before initialised) */ +static gchar * +g_build_home_dir (void) +{ + gchar *home_dir; + + /* We first check HOME and use it if it is set */ + home_dir = g_strdup (g_getenv ("HOME")); + +#ifdef G_OS_WIN32 + /* Only believe HOME if it is an absolute path and exists. + * + * We only do this check on Windows for a couple of reasons. + * Historically, we only did it there because we used to ignore $HOME + * on UNIX. There are concerns about enabling it now on UNIX because + * of things like autofs. In short, if the user has a bogus value in + * $HOME then they get what they pay for... + */ + if (home_dir != NULL) + { + if (!(g_path_is_absolute (home_dir) && + g_file_test (home_dir, G_FILE_TEST_IS_DIR))) + g_clear_pointer (&home_dir, g_free); + } + + /* In case HOME is Unix-style (it happens), convert it to + * Windows style. + */ + if (home_dir != NULL) + { + gchar *p; + while ((p = strchr (home_dir, '/')) != NULL) + *p = '\\'; + } + + if (home_dir == NULL) + { + /* USERPROFILE is probably the closest equivalent to $HOME? */ + if (g_getenv ("USERPROFILE") != NULL) + home_dir = g_strdup (g_getenv ("USERPROFILE")); + } + + if (home_dir == NULL) + home_dir = get_special_folder (CSIDL_PROFILE); + + if (home_dir == NULL) + home_dir = get_windows_directory_root (); +#endif /* G_OS_WIN32 */ + + if (home_dir == NULL) + { + /* If we didn't get it from any of those methods, we will have + * to read the user database entry. + */ + UserDatabaseEntry *entry = g_get_user_database_entry (); + home_dir = g_strdup (entry->home_dir); + } + + /* If we have been denied access to /etc/passwd (for example, by an + * overly-zealous LSM), make up a junk value. The return value at this + * point is explicitly documented as ‘undefined’. */ + if (home_dir == NULL) + { + g_warning ("Could not find home directory: $HOME is not set, and " + "user database could not be read."); + home_dir = g_strdup ("/"); + } + + return g_steal_pointer (&home_dir); +} + /** * g_get_home_dir: * @@ -829,85 +899,7 @@ g_get_home_dir (void) G_LOCK (g_utils_global); if (g_home_dir == NULL) - { - gchar *tmp; - - /* We first check HOME and use it if it is set */ - tmp = g_strdup (g_getenv ("HOME")); - -#ifdef G_OS_WIN32 - /* Only believe HOME if it is an absolute path and exists. - * - * We only do this check on Windows for a couple of reasons. - * Historically, we only did it there because we used to ignore $HOME - * on UNIX. There are concerns about enabling it now on UNIX because - * of things like autofs. In short, if the user has a bogus value in - * $HOME then they get what they pay for... - */ - if (tmp) - { - if (!(g_path_is_absolute (tmp) && - g_file_test (tmp, G_FILE_TEST_IS_DIR))) - { - g_free (tmp); - tmp = NULL; - } - } - - /* In case HOME is Unix-style (it happens), convert it to - * Windows style. - */ - if (tmp) - { - gchar *p; - while ((p = strchr (tmp, '/')) != NULL) - *p = '\\'; - } - - if (!tmp) - { - /* USERPROFILE is probably the closest equivalent to $HOME? */ - if (g_getenv ("USERPROFILE") != NULL) - tmp = g_strdup (g_getenv ("USERPROFILE")); - } - - if (!tmp) - tmp = get_special_folder (CSIDL_PROFILE); - - if (!tmp) - tmp = get_windows_directory_root (); -#endif /* G_OS_WIN32 */ - - if (!tmp) - { - /* If we didn't get it from any of those methods, we will have - * to read the user database entry. - */ - UserDatabaseEntry *entry; - - entry = g_get_user_database_entry (); - - /* Strictly speaking, we should copy this, but we know that - * neither will ever be freed, so don't bother... - */ - tmp = entry->home_dir; - } - - /* If we have been denied access to /etc/passwd (for example, by an - * overly-zealous LSM), make up a junk value. The return value at this - * point is explicitly documented as ‘undefined’. Memory management is as - * immediately above: strictly this should be copied, but we know not - * copying it is OK. */ - if (tmp == NULL) - { - g_warning ("Could not find home directory: $HOME is not set, and " - "user database could not be read."); - tmp = "/"; - } - - g_home_dir = g_steal_pointer (&tmp); - } - + g_home_dir = g_build_home_dir (); home_dir = g_home_dir; G_UNLOCK (g_utils_global); @@ -1299,12 +1291,14 @@ g_build_user_data_dir (void) #endif if (!data_dir || !data_dir[0]) { - const gchar *home_dir = g_get_home_dir (); + gchar *home_dir = g_build_home_dir (); - if (home_dir) + if (home_dir != NULL) data_dir = g_build_filename (home_dir, ".local", "share", NULL); else data_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".local", "share", NULL); + + g_free (home_dir); } return g_steal_pointer (&data_dir); @@ -1362,12 +1356,14 @@ g_build_user_config_dir (void) #endif if (!config_dir || !config_dir[0]) { - const gchar *home_dir = g_get_home_dir (); + gchar *home_dir = g_build_home_dir (); - if (home_dir) + if (home_dir != NULL) config_dir = g_build_filename (home_dir, ".config", NULL); else config_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".config", NULL); + + g_free (home_dir); } return g_steal_pointer (&config_dir); @@ -1425,12 +1421,14 @@ g_build_user_cache_dir (void) #endif if (!cache_dir || !cache_dir[0]) { - const gchar *home_dir = g_get_home_dir (); + gchar *home_dir = g_build_home_dir (); - if (home_dir) + if (home_dir != NULL) cache_dir = g_build_filename (home_dir, ".cache", NULL); else cache_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".cache", NULL); + + g_free (home_dir); } return g_steal_pointer (&cache_dir); @@ -1805,7 +1803,9 @@ load_user_special_dirs (void) if (is_relative) { - g_user_special_dirs[directory] = g_build_filename (g_get_home_dir (), d, NULL); + gchar *home_dir = g_build_home_dir (); + g_user_special_dirs[directory] = g_build_filename (home_dir, d, NULL); + g_free (home_dir); } else g_user_special_dirs[directory] = g_strdup (d); @@ -1913,7 +1913,11 @@ g_get_user_special_dir (GUserDirectory directory) /* Special-case desktop for historical compatibility */ if (g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] == NULL) - g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = g_build_filename (g_get_home_dir (), "Desktop", NULL); + { + gchar *home_dir = g_build_home_dir (); + g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = g_build_filename (home_dir, "Desktop", NULL); + g_free (home_dir); + } } user_special_dir = g_user_special_dirs[directory]; From b4de2e32975ec2d63ebae20a29d3acc126fdabd2 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 14:06:57 +0000 Subject: [PATCH 18/32] glib.supp: Add various one-time allocations to the suppression file Signed-off-by: Philip Withnall --- glib.supp | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/glib.supp b/glib.supp index 3ff1fdbc0..c8b184e5c 100644 --- a/glib.supp +++ b/glib.supp @@ -229,13 +229,21 @@ } { - g-get-language-names + g-get-language-names-malloc Memcheck:Leak fun:malloc ... fun:g_get_language_names } +{ + g-get-language-names-calloc + Memcheck:Leak + fun:calloc + ... + fun:g_get_language_names +} + { g-static-mutex Memcheck:Leak @@ -582,4 +590,70 @@ ... fun:g_build_user_data_dir fun:g_get_user_data_dir +} + +# gdesktopappinfo.c caches a one-time allocation global table of @desktop_file_dirs. +{ + desktop_file_dirs_malloc + Memcheck:Leak + fun:malloc + ... + fun:desktop_file_dirs_lock +} + +# gdesktopappinfo.c caches a one-time allocation global table of @desktop_file_dirs. +{ + desktop_file_dirs_realloc + Memcheck:Leak + fun:realloc + ... + fun:desktop_file_dirs_lock +} + +# gdesktopappinfo.c caches a one-time allocation global table of @desktop_file_dirs. +{ + desktop_file_dir_unindexed_setup_search + Memcheck:Leak + fun:malloc + ... + fun:desktop_file_dir_unindexed_setup_search + fun:desktop_file_dir_unindexed_setup_search +} + +# g_io_extension_point_register() caches a one-time allocation global table of @extension_points. +{ + g_io_extension_point_register + Memcheck:Leak + fun:calloc + ... + fun:g_io_extension_point_register +} + +# g_strerror() caches a one-time allocation global table of @errors. +{ + g_strerror + Memcheck:Leak + fun:malloc + ... + fun:g_locale_to_utf8 + fun:g_strerror +} + +# g_socket_connection_factory_register_type() caches a one-time allocation global table of @connection_types. +{ + g_socket_connection_factory_register_type + Memcheck:Leak + fun:calloc + ... + fun:g_socket_connection_factory_register_type +} + +# g_dbus_error_quark() never unregisters itself as a GDBusError domain, as it’s always available +{ + g_dbus_error_quark + Memcheck:Leak + fun:calloc + ... + fun:g_dbus_error_register_error_domain + fun:g_dbus_error_quark } \ No newline at end of file From b6f7f3742799e55c52e431ff26e05e4493e0aee8 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 16:24:07 +0000 Subject: [PATCH 19/32] xdgmime: Add xdg_mime_set_dirs() method to override XDG envvars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to make xdgmime properly relocatable so that unit tests can use it without it reading and modifying the user’s actual xdgmime files, and without the need to call setenv() (and get tied up with thread safety problems), add a xdg_mime_set_dirs() method to allow the dirs to be overridden. They will still default to the values of $XDG_DATA_HOME and $XDG_DATA_DIRS. Signed-off-by: Philip Withnall --- gio/xdgmime/xdgmime.c | 196 ++++++++++++++++++++++++++++-------------- gio/xdgmime/xdgmime.h | 2 + 2 files changed, 132 insertions(+), 66 deletions(-) diff --git a/gio/xdgmime/xdgmime.c b/gio/xdgmime/xdgmime.c index 12028927d..2446d3813 100644 --- a/gio/xdgmime/xdgmime.c +++ b/gio/xdgmime/xdgmime.c @@ -56,6 +56,8 @@ static XdgCallbackList *callback_list = NULL; static XdgIconList *icon_list = NULL; static XdgIconList *generic_icon_list = NULL; +static char **xdg_dirs = NULL; /* NULL terminated */ + XdgMimeCache **_caches = NULL; static int n_caches = 0; @@ -139,8 +141,8 @@ xdg_mime_init_from_directory (const char *directory) assert (directory != NULL); - file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); + file_name = malloc (strlen (directory) + strlen ("/mime.cache") + 1); + strcpy (file_name, directory); strcat (file_name, "/mime.cache"); if (stat (file_name, &st) == 0) { XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name); @@ -159,8 +161,8 @@ xdg_mime_init_from_directory (const char *directory) } free (file_name); - file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs2"); + file_name = malloc (strlen (directory) + strlen ("/globs2") + 1); + strcpy (file_name, directory); strcat (file_name, "/globs2"); if (stat (file_name, &st) == 0) { _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE); @@ -169,8 +171,8 @@ xdg_mime_init_from_directory (const char *directory) else { free (file_name); - file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs"); + file_name = malloc (strlen (directory) + strlen ("/globs") + 1); + strcpy (file_name, directory); strcat (file_name, "/globs"); if (stat (file_name, &st) == 0) { _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE); @@ -182,8 +184,8 @@ xdg_mime_init_from_directory (const char *directory) } } - file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/magic"); + file_name = malloc (strlen (directory) + strlen ("/magic") + 1); + strcpy (file_name, directory); strcat (file_name, "/magic"); if (stat (file_name, &st) == 0) { _xdg_mime_magic_read_from_file (global_magic, file_name); @@ -194,69 +196,81 @@ xdg_mime_init_from_directory (const char *directory) free (file_name); } - file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/aliases"); + file_name = malloc (strlen (directory) + strlen ("/aliases") + 1); + strcpy (file_name, directory); strcat (file_name, "/aliases"); _xdg_mime_alias_read_from_file (alias_list, file_name); free (file_name); - file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/subclasses"); + file_name = malloc (strlen (directory) + strlen ("/subclasses") + 1); + strcpy (file_name, directory); strcat (file_name, "/subclasses"); _xdg_mime_parent_read_from_file (parent_list, file_name); free (file_name); - file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/icons"); + file_name = malloc (strlen (directory) + strlen ("/icons") + 1); + strcpy (file_name, directory); strcat (file_name, "/icons"); _xdg_mime_icon_read_from_file (icon_list, file_name); free (file_name); - file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons"); + file_name = malloc (strlen (directory) + strlen ("/generic-icons") + 1); + strcpy (file_name, directory); strcat (file_name, "/generic-icons"); _xdg_mime_icon_read_from_file (generic_icon_list, file_name); free (file_name); return FALSE; /* Keep processing */ } -/* Runs a command on all the directories in the search path */ +/* Set @xdg_dirs from the environment. It must not have been set already. */ static void -xdg_run_command_on_dirs (XdgDirectoryFunc func, - void *user_data) +xdg_init_dirs (void) { - const char *xdg_data_home; - const char *xdg_data_dirs; + const char *xdg_data_home, *home, *xdg_data_dirs; const char *ptr; + size_t n_dirs = 0; + size_t i, current_dir; + + assert (xdg_dirs == NULL); xdg_data_home = getenv ("XDG_DATA_HOME"); - if (xdg_data_home) - { - if ((func) (xdg_data_home, user_data)) - return; - } - else - { - const char *home; - - home = getenv ("HOME"); - if (home != NULL) - { - char *guessed_xdg_home; - int stop_processing; - - guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1); - strcpy (guessed_xdg_home, home); - strcat (guessed_xdg_home, "/.local/share/"); - stop_processing = (func) (guessed_xdg_home, user_data); - free (guessed_xdg_home); - - if (stop_processing) - return; - } - } - + home = getenv ("HOME"); xdg_data_dirs = getenv ("XDG_DATA_DIRS"); + if (xdg_data_dirs == NULL) xdg_data_dirs = "/usr/local/share/:/usr/share/"; + /* Work out how many dirs we’re dealing with. */ + if (xdg_data_home != NULL || home != NULL) + n_dirs++; + n_dirs++; /* initial entry in @xdg_data_dirs */ + for (i = 0; xdg_data_dirs[i] != '\0'; i++) + if (xdg_data_dirs[i] == ':') + n_dirs++; + + xdg_dirs = calloc (n_dirs + 1 /* NULL terminator */, sizeof (char *)); + current_dir = 0; + + /* $XDG_DATA_HOME */ + if (xdg_data_home != NULL) + { + char *mime_subdir; + + mime_subdir = malloc (strlen (xdg_data_home) + strlen ("/mime/") + 1); + strcpy (mime_subdir, xdg_data_home); + strcat (mime_subdir, "/mime/"); + + xdg_dirs[current_dir++] = mime_subdir; + } + else if (home != NULL) + { + char *guessed_xdg_home; + + guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/mime/") + 1); + strcpy (guessed_xdg_home, home); + strcat (guessed_xdg_home, "/.local/share/mime/"); + + xdg_dirs[current_dir++] = guessed_xdg_home; + } + + /* $XDG_DATA_DIRS */ ptr = xdg_data_dirs; while (*ptr != '\000') @@ -264,33 +278,83 @@ xdg_run_command_on_dirs (XdgDirectoryFunc func, const char *end_ptr; char *dir; int len; - int stop_processing; end_ptr = ptr; while (*end_ptr != ':' && *end_ptr != '\000') - end_ptr ++; + end_ptr ++; if (end_ptr == ptr) - { - ptr++; - continue; - } + { + ptr++; + continue; + } if (*end_ptr == ':') - len = end_ptr - ptr; + len = end_ptr - ptr; else - len = end_ptr - ptr + 1; - dir = malloc (len + 1); + len = end_ptr - ptr + 1; + dir = malloc (len + strlen ("/mime/") + 1); strncpy (dir, ptr, len); dir[len] = '\0'; - stop_processing = (func) (dir, user_data); - free (dir); + strcat (dir, "/mime/"); - if (stop_processing) - return; + xdg_dirs[current_dir++] = dir; ptr = end_ptr; } + + /* NULL terminator */ + xdg_dirs[current_dir] = NULL; + + need_reread = TRUE; +} + +/* Runs a command on all the directories in the search path (@xdg_dirs). */ +static void +xdg_run_command_on_dirs (XdgDirectoryFunc func, + void *user_data) +{ + size_t i; + + if (xdg_dirs == NULL) + xdg_init_dirs (); + + for (i = 0; xdg_dirs[i] != NULL; i++) + { + if ((func) (xdg_dirs[i], user_data)) + return; + } +} + +/* Allows the calling code to override the directories used by xdgmime, without + * having to change environment variables in a running process (which is not + * thread safe). This is intended to be used by tests. The changes will be + * picked up by xdg_mime_init() next time public API is called. + * + * This will set @xdg_dirs. Directories in @dirs must be complete, including + * the conventional `/mime` subdirectory. This is to allow tests to override + * them without the need to create a subdirectory. */ +void +xdg_mime_set_dirs (const char * const *dirs) +{ + size_t i; + + for (i = 0; xdg_dirs != NULL && xdg_dirs[i] != NULL; i++) + free (xdg_dirs[i]); + if (xdg_dirs != NULL) + free (xdg_dirs[i]); + xdg_dirs = NULL; + + if (dirs != NULL) + { + for (i = 0; dirs[i] != NULL; i++); + xdg_dirs = calloc (i + 1 /* NULL terminator */, sizeof (char*)); + for (i = 0; dirs[i] != NULL; i++) + xdg_dirs[i] = strdup (dirs[i]); + xdg_dirs[i] = NULL; + } + + need_reread = TRUE; } /* Checks file_path to make sure it has the same mtime as last time it was @@ -344,8 +408,8 @@ xdg_check_dir (const char *directory, assert (directory != NULL); /* Check the mime.cache file */ - file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); + file_name = malloc (strlen (directory) + strlen ("/mime.cache") + 1); + strcpy (file_name, directory); strcat (file_name, "/mime.cache"); invalid = xdg_check_file (file_name, &exists); free (file_name); if (invalid) @@ -359,8 +423,8 @@ xdg_check_dir (const char *directory, } /* Check the globs file */ - file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs"); + file_name = malloc (strlen (directory) + strlen ("/globs") + 1); + strcpy (file_name, directory); strcat (file_name, "/globs"); invalid = xdg_check_file (file_name, NULL); free (file_name); if (invalid) @@ -370,8 +434,8 @@ xdg_check_dir (const char *directory, } /* Check the magic file */ - file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/magic"); + file_name = malloc (strlen (directory) + strlen ("/magic") + 1); + strcpy (file_name, directory); strcat (file_name, "/magic"); invalid = xdg_check_file (file_name, NULL); free (file_name); if (invalid) diff --git a/gio/xdgmime/xdgmime.h b/gio/xdgmime/xdgmime.h index cd20c77a9..b175de107 100644 --- a/gio/xdgmime/xdgmime.h +++ b/gio/xdgmime/xdgmime.h @@ -127,6 +127,8 @@ int xdg_mime_register_reload_callback (XdgMimeCallback callback, void xdg_mime_remove_callback (int callback_id); #endif +void xdg_mime_set_dirs (const char * const *dirs); + /* Private versions of functions that don't call xdg_mime_init () */ int _xdg_mime_mime_type_equal (const char *mime_a, const char *mime_b); From df2f13f89b36b5095d3e3e70c610aa5408a4e522 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 16:39:37 +0000 Subject: [PATCH 20/32] gcontenttype: Improve formatting of gcontenttype section documentation Signed-off-by: Philip Withnall --- gio/gcontenttype.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c index 57249d94e..60228e935 100644 --- a/gio/gcontenttype.c +++ b/gio/gcontenttype.c @@ -41,13 +41,13 @@ * * A content type is a platform specific string that defines the type * of a file. On UNIX it is a - * [mime type](http://www.wikipedia.org/wiki/Internet_media_type) - * like "text/plain" or "image/png". - * On Win32 it is an extension string like ".doc", ".txt" or a perceived - * string like "audio". Such strings can be looked up in the registry at - * HKEY_CLASSES_ROOT. - * On OSX it is a [Uniform Type Identifier](https://en.wikipedia.org/wiki/Uniform_Type_Identifier) - * such as "com.apple.application". + * [MIME type](http://www.wikipedia.org/wiki/Internet_media_type) + * like `text/plain` or `image/png`. + * On Win32 it is an extension string like `.doc`, `.txt` or a perceived + * string like `audio`. Such strings can be looked up in the registry at + * `HKEY_CLASSES_ROOT`. + * On macOS it is a [Uniform Type Identifier](https://en.wikipedia.org/wiki/Uniform_Type_Identifier) + * such as `com.apple.application`. **/ #include From b06fa34bb1cb3eb33bae819ebedd1689b3c29f0f Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 16:55:48 +0000 Subject: [PATCH 21/32] gcontenttype: Add g_content_type_{get,set}_mime_dirs() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the list of directories which contain MIME data to be set, separately from the list of directories returned by g_get_user_data_home() and g_get_system_data_dirs(). While the latter are overridden for a unit test, we don’t have access to the system MIME registry, which can sometimes be useful for tests which need to know about standard MIME associations from shared-mime-info. Allow g_content_type_set_mime_dirs() to be used from unit tests to allow them to use the system MIME registry again, or to allow them to be pointed to another custom registry. Signed-off-by: Philip Withnall --- gio/gcontenttype.c | 153 ++++++++++++++++++++++++++++++++++++--------- gio/gcontenttype.h | 6 ++ 2 files changed, 129 insertions(+), 30 deletions(-) diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c index 60228e935..c34349575 100644 --- a/gio/gcontenttype.c +++ b/gio/gcontenttype.c @@ -55,6 +55,8 @@ #define XDG_PREFIX _gio_xdg #include "xdgmime/xdgmime.h" +static void tree_magic_schedule_reload (void); + /* We lock this mutex whenever we modify global state in this module. */ G_LOCK_DEFINE_STATIC (gio_xdgmime); @@ -111,6 +113,106 @@ _g_unix_content_type_get_parents (const gchar *type) return (gchar **)g_ptr_array_free (array, FALSE); } +G_LOCK_DEFINE_STATIC (global_mime_dirs); +static gchar **global_mime_dirs = NULL; + +static void +_g_content_type_set_mime_dirs_locked (const char * const *dirs) +{ + g_clear_pointer (&global_mime_dirs, g_strfreev); + + if (dirs != NULL) + { + global_mime_dirs = g_strdupv ((gchar **) dirs); + } + else + { + GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (g_free); + const gchar * const *system_dirs = g_get_system_data_dirs (); + + g_ptr_array_add (mime_dirs, g_build_filename (g_get_user_data_dir (), "mime", NULL)); + for (; *system_dirs != NULL; system_dirs++) + g_ptr_array_add (mime_dirs, g_build_filename (*system_dirs, "mime", NULL)); + g_ptr_array_add (mime_dirs, NULL); /* NULL terminator */ + + global_mime_dirs = (gchar **) g_ptr_array_free (mime_dirs, FALSE); + } + + xdg_mime_set_dirs ((const gchar * const *) global_mime_dirs); + tree_magic_schedule_reload (); +} + +/** + * g_content_type_set_mime_dirs: + * @dirs: (array zero-terminated=1) (nullable): %NULL-terminated list of + * directories to load MIME data from, including any `mime/` subdirectory, + * and with the first directory to try listed first + * + * Set the list of directories used by GIO to load the MIME database. + * If @dirs is %NULL, the directories used are the default: + * + * - the `mime` subdirectory of the directory in `$XDG_DATA_HOME` + * - the `mime` subdirectory of every directory in `$XDG_DATA_DIRS` + * + * This function is intended to be used when writing tests that depend on + * information stored in the MIME database, in order to control the data. + * + * Typically, in case your tests use %G_TEST_OPTION_ISOLATE_DIRS, but they + * depend on the system’s MIME database, you should call this function + * with @dirs set to %NULL before calling g_test_init(), for instance: + * + * |[ + * // Load MIME data from the system + * g_content_type_set_mime_dirs (NULL); + * // Isolate the environment + * g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); + * + * … + * + * return g_test_run (); + * ]| + * + * Since: 2.60 + */ +/*< private >*/ +void +g_content_type_set_mime_dirs (const gchar * const *dirs) +{ + G_LOCK (global_mime_dirs); + _g_content_type_set_mime_dirs_locked (dirs); + G_UNLOCK (global_mime_dirs); +} + +/** + * g_content_type_get_mime_dirs: + * + * Get the list of directories which MIME data is loaded from. See + * g_content_type_set_mime_dirs() for details. + * + * Returns: (transfer none) (array zero-terminated=1): %NULL-terminated list of + * directories to load MIME data from, including any `mime/` subdirectory, + * and with the first directory to try listed first + * Since: 2.60 + */ +/*< private >*/ +const gchar * const * +g_content_type_get_mime_dirs (void) +{ + const gchar * const *mime_dirs; + + G_LOCK (global_mime_dirs); + + if (global_mime_dirs == NULL) + _g_content_type_set_mime_dirs_locked (NULL); + + mime_dirs = (const gchar * const *) global_mime_dirs; + + G_UNLOCK (global_mime_dirs); + + g_assert (mime_dirs != NULL); + return mime_dirs; +} + /** * g_content_type_equals: * @type1: a content type string @@ -306,7 +408,7 @@ load_comment_for_mime_helper (const char *dir, mime_info_text }; - filename = g_build_filename (dir, "mime", basename, NULL); + filename = g_build_filename (dir, basename, NULL); res = g_file_get_contents (filename, &data, &len, NULL); g_free (filename); @@ -328,22 +430,14 @@ load_comment_for_mime_helper (const char *dir, static char * load_comment_for_mime (const char *mimetype) { - const char * const* dirs; + const char * const *dirs; char *basename; char *comment; - int i; + gsize i; basename = g_strdup_printf ("%s.xml", mimetype); - comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename); - if (comment) - { - g_free (basename); - return comment; - } - - dirs = g_get_system_data_dirs (); - + dirs = g_content_type_get_mime_dirs (); for (i = 0; dirs[i] != NULL; i++) { comment = load_comment_for_mime_helper (dirs[i], basename); @@ -780,10 +874,10 @@ enumerate_mimetypes_dir (const char *dir, { DIR *d; struct dirent *ent; - char *mimedir; + const char *mimedir; char *name; - mimedir = g_build_filename (dir, "mime", NULL); + mimedir = dir; d = opendir (mimedir); if (d) @@ -800,8 +894,6 @@ enumerate_mimetypes_dir (const char *dir, } closedir (d); } - - g_free (mimedir); } /** @@ -817,18 +909,16 @@ enumerate_mimetypes_dir (const char *dir, GList * g_content_types_get_registered (void) { - const char * const* dirs; + const char * const *dirs; GHashTable *mimetypes; GHashTableIter iter; gpointer key; - int i; + gsize i; GList *l; mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes); - dirs = g_get_system_data_dirs (); - + dirs = g_content_type_get_mime_dirs (); for (i = 0; dirs[i] != NULL; i++) enumerate_mimetypes_dir (dirs[i], mimetypes); @@ -1030,7 +1120,7 @@ read_tree_magic_from_directory (const gchar *prefix) TreeMatchlet *matchlet; gint depth; - filename = g_build_filename (prefix, "mime", "treemagic", NULL); + filename = g_build_filename (prefix, "treemagic", NULL); if (g_file_get_contents (filename, &text, &len, NULL)) { @@ -1068,11 +1158,16 @@ read_tree_magic_from_directory (const gchar *prefix) g_free (filename); } +static void +tree_magic_schedule_reload (void) +{ + need_reload = TRUE; +} static void xdg_mime_reload (void *user_data) { - need_reload = TRUE; + tree_magic_schedule_reload (); } static void @@ -1086,9 +1181,7 @@ static void tree_magic_init (void) { static gboolean initialized = FALSE; - const gchar *dir; - const gchar * const * dirs; - int i; + gsize i; if (!initialized) { @@ -1100,14 +1193,14 @@ tree_magic_init (void) if (need_reload) { + const char * const *dirs; + need_reload = FALSE; tree_magic_shutdown (); - dir = g_get_user_data_dir (); - read_tree_magic_from_directory (dir); - dirs = g_get_system_data_dirs (); - for (i = 0; dirs[i]; i++) + dirs = g_content_type_get_mime_dirs (); + for (i = 0; dirs[i] != NULL; i++) read_tree_magic_from_directory (dirs[i]); } } diff --git a/gio/gcontenttype.h b/gio/gcontenttype.h index d87672dd5..d970acbe1 100644 --- a/gio/gcontenttype.h +++ b/gio/gcontenttype.h @@ -69,6 +69,12 @@ gchar ** g_content_type_guess_for_tree (GFile *root); GLIB_AVAILABLE_IN_ALL GList * g_content_types_get_registered (void); +/*< private >*/ +GLIB_AVAILABLE_IN_2_60 +const gchar * const *g_content_type_get_mime_dirs (void); +GLIB_AVAILABLE_IN_2_60 +void g_content_type_set_mime_dirs (const gchar * const *dirs); + G_END_DECLS #endif /* __G_CONTENT_TYPE_H__ */ From e97bf89dd4bc7c5711f3b5e791932af09c879657 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 16:59:10 +0000 Subject: [PATCH 22/32] gcontenttype: Improve the formatting of some code in a docs comment Signed-off-by: Philip Withnall --- gio/gcontenttype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c index c34349575..04d278db3 100644 --- a/gio/gcontenttype.c +++ b/gio/gcontenttype.c @@ -901,7 +901,7 @@ enumerate_mimetypes_dir (const char *dir, * * Gets a list of strings containing all the registered content types * known to the system. The list and its data should be freed using - * g_list_free_full (list, g_free). + * `g_list_free_full (list, g_free)`. * * Returns: (element-type utf8) (transfer full): list of the registered * content types From 99bc33b632f434e160b4453498e0d15de50778f6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 13 Dec 2018 17:01:19 +0000 Subject: [PATCH 23/32] gdesktopappinfo: Reload the desktop dirs if the $XDG_CONFIG_HOME changes This causes the desktop directory cache to be correctly reloaded between unit tests if G_TEST_OPTION_ISOLATE_DIRS is in use. Signed-off-by: Philip Withnall --- gio/gdesktopappinfo.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 0741c0eff..9ebfce4f0 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -152,6 +152,7 @@ typedef struct static DesktopFileDir *desktop_file_dirs; static guint n_desktop_file_dirs; +static const gchar *desktop_file_dirs_config_dir = NULL; static const guint desktop_file_dir_user_config_index = 0; static guint desktop_file_dir_user_data_index; static GMutex desktop_file_dir_lock; @@ -1476,9 +1477,24 @@ static void desktop_file_dirs_lock (void) { gint i; + const gchar *user_config_dir = g_get_user_config_dir (); g_mutex_lock (&desktop_file_dir_lock); + /* If the XDG dirs configuration has changed (expected only during tests), + * clear and reload the state. */ + if (g_strcmp0 (desktop_file_dirs_config_dir, user_config_dir) != 0) + { + g_debug ("%s: Resetting desktop app info dirs from %s to %s", + G_STRFUNC, desktop_file_dirs_config_dir, user_config_dir); + + for (i = 0; i < n_desktop_file_dirs; i++) + desktop_file_dir_reset (&desktop_file_dirs[i]); + g_clear_pointer (&desktop_file_dirs, g_free); + n_desktop_file_dirs = 0; + desktop_file_dir_user_data_index = 0; + } + if (desktop_file_dirs == NULL) { const char * const *dirs; @@ -1488,7 +1504,7 @@ desktop_file_dirs_lock (void) tmp = g_array_new (FALSE, FALSE, sizeof (DesktopFileDir)); /* First, the configs. Highest priority: the user's ~/.config */ - desktop_file_dir_create_for_config (tmp, g_get_user_config_dir ()); + desktop_file_dir_create_for_config (tmp, user_config_dir); /* Next, the system configs (/etc/xdg, and so on). */ dirs = g_get_system_config_dirs (); @@ -1504,9 +1520,11 @@ desktop_file_dirs_lock (void) for (i = 0; dirs[i]; i++) desktop_file_dir_create (tmp, dirs[i]); - /* The list of directories will never change after this. */ + /* The list of directories will never change after this, unless + * g_get_user_config_dir() changes due to %G_TEST_OPTION_ISOLATE_DIRS. */ desktop_file_dirs = (DesktopFileDir *) tmp->data; n_desktop_file_dirs = tmp->len; + desktop_file_dirs_config_dir = user_config_dir; g_array_free (tmp, FALSE); } @@ -3191,6 +3209,8 @@ ensure_dir (DirType type, g_assert_not_reached (); } + g_debug ("%s: Ensuring %s", G_STRFUNC, path); + errno = 0; if (g_mkdir_with_parents (path, 0700) == 0) return path; From 797a8b0930cb3fb7ed9b58893e905da373d17caa Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 17:57:43 +0000 Subject: [PATCH 24/32] tests: Isolate directories in appinfo test This is essentially a reversion of commit a39b847ddf3fe63fd5f7e1134b46fc888c10df5e, plus the addition of the new G_TEST_OPTIONS_ISOLATE_XDG_DIRS option. Signed-off-by: Philip Withnall https://gitlab.gnome.org/GNOME/glib/issues/1601 --- gio/tests/appinfo.c | 177 ++++++++------------------------------------ 1 file changed, 29 insertions(+), 148 deletions(-) diff --git a/gio/tests/appinfo.c b/gio/tests/appinfo.c index 971b7eb83..6e081832f 100644 --- a/gio/tests/appinfo.c +++ b/gio/tests/appinfo.c @@ -6,111 +6,6 @@ #include #include -static gboolean -cleanup_dir_recurse (GFile *parent, - GFile *root, - GError **error) -{ - gboolean ret = FALSE; - GFileEnumerator *enumerator; - GError *local_error = NULL; - - g_assert (root != NULL); - - enumerator = - g_file_enumerate_children (parent, "*", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, - &local_error); - if (!enumerator) - { - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - g_clear_error (&local_error); - ret = TRUE; - } - goto out; - } - - while (TRUE) - { - GFile *child; - GFileInfo *finfo; - char *relative_path; - - if (!g_file_enumerator_iterate (enumerator, &finfo, &child, NULL, error)) - goto out; - if (!finfo) - break; - - relative_path = g_file_get_relative_path (root, child); - g_assert (relative_path != NULL); - g_free (relative_path); - - if (g_file_info_get_file_type (finfo) == G_FILE_TYPE_DIRECTORY) - { - if (!cleanup_dir_recurse (child, root, error)) - goto out; - } - - if (!g_file_delete (child, NULL, error)) - goto out; - } - - ret = TRUE; - out: - g_clear_object (&enumerator); - - return ret; -} - -typedef struct -{ - gchar *tmp_dir; - gchar *config_dir; - gchar *data_dir; -} Fixture; - -static void -setup (Fixture *fixture, - gconstpointer user_data) -{ - GError *local_error = NULL; - - fixture->tmp_dir = g_dir_make_tmp ("gio-test-appinfo_XXXXXX", &local_error); - g_assert_no_error (local_error); - - fixture->config_dir = g_build_filename (fixture->tmp_dir, "config", NULL); - g_assert_cmpint (g_mkdir (fixture->config_dir, 0755), ==, 0); - - fixture->data_dir = g_build_filename (fixture->tmp_dir, "data", NULL); - g_assert_cmpint (g_mkdir (fixture->data_dir, 0755), ==, 0); - - g_setenv ("XDG_CONFIG_HOME", fixture->config_dir, TRUE); - g_setenv ("XDG_DATA_HOME", fixture->data_dir, TRUE); - - g_setenv ("XDG_DATA_DIRS", "/dev/null", TRUE); - g_setenv ("XDG_CONFIG_DIRS", "/dev/null", TRUE); - g_setenv ("XDG_CACHE_HOME", "/dev/null", TRUE); - g_setenv ("XDG_RUNTIME_DIR", "/dev/null", TRUE); - - g_test_message ("Using tmp directory: %s", fixture->tmp_dir); -} - -static void -teardown (Fixture *fixture, - gconstpointer user_data) -{ - GFile *tmp_dir = g_file_new_for_path (fixture->tmp_dir); - GError *local_error = NULL; - - cleanup_dir_recurse (tmp_dir, tmp_dir, &local_error); - g_assert_no_error (local_error); - - g_clear_pointer (&fixture->config_dir, g_free); - g_clear_pointer (&fixture->data_dir, g_free); - g_clear_pointer (&fixture->tmp_dir, g_free); -} - static void test_launch_for_app_info (GAppInfo *appinfo) { @@ -155,8 +50,7 @@ test_launch_for_app_info (GAppInfo *appinfo) } static void -test_launch (Fixture *fixture, - gconstpointer user_data) +test_launch (void) { GAppInfo *appinfo; const gchar *path; @@ -170,8 +64,7 @@ test_launch (Fixture *fixture, } static void -test_launch_no_app_id (Fixture *fixture, - gconstpointer user_data) +test_launch_no_app_id (void) { const gchar desktop_file_base_contents[] = "[Desktop Entry]\n" @@ -278,8 +171,7 @@ test_locale (const char *locale) } static void -test_text (Fixture *fixture, - gconstpointer user_data) +test_text (void) { test_locale ("C"); test_locale ("en_US"); @@ -288,8 +180,7 @@ test_text (Fixture *fixture, } static void -test_basic (Fixture *fixture, - gconstpointer user_data) +test_basic (void) { GAppInfo *appinfo; GAppInfo *appinfo2; @@ -317,8 +208,7 @@ test_basic (Fixture *fixture, } static void -test_show_in (Fixture *fixture, - gconstpointer user_data) +test_show_in (void) { GAppInfo *appinfo; const gchar *path; @@ -340,8 +230,7 @@ test_show_in (Fixture *fixture, } static void -test_commandline (Fixture *fixture, - gconstpointer user_data) +test_commandline (void) { GAppInfo *appinfo; GError *error; @@ -387,8 +276,7 @@ test_commandline (Fixture *fixture, } static void -test_launch_context (Fixture *fixture, - gconstpointer user_data) +test_launch_context (void) { GAppLaunchContext *context; GAppInfo *appinfo; @@ -440,8 +328,7 @@ launch_failed (GAppLaunchContext *context, } static void -test_launch_context_signals (Fixture *fixture, - gconstpointer user_data) +test_launch_context_signals (void) { GAppLaunchContext *context; GAppInfo *appinfo; @@ -471,8 +358,7 @@ test_launch_context_signals (Fixture *fixture, } static void -test_tryexec (Fixture *fixture, - gconstpointer user_data) +test_tryexec (void) { GAppInfo *appinfo; const gchar *path; @@ -487,8 +373,7 @@ test_tryexec (Fixture *fixture, * file extension, and also add and remove handled mime types. */ static void -test_associations (Fixture *fixture, - gconstpointer user_data) +test_associations (void) { GAppInfo *appinfo; GAppInfo *appinfo2; @@ -549,8 +434,7 @@ test_associations (Fixture *fixture, } static void -test_environment (Fixture *fixture, - gconstpointer user_data) +test_environment (void) { GAppLaunchContext *ctx; gchar **env; @@ -595,8 +479,7 @@ test_environment (Fixture *fixture, } static void -test_startup_wm_class (Fixture *fixture, - gconstpointer user_data) +test_startup_wm_class (void) { GDesktopAppInfo *appinfo; const char *wm_class; @@ -612,8 +495,7 @@ test_startup_wm_class (Fixture *fixture, } static void -test_supported_types (Fixture *fixture, - gconstpointer user_data) +test_supported_types (void) { GAppInfo *appinfo; const char * const *content_types; @@ -630,8 +512,7 @@ test_supported_types (Fixture *fixture, } static void -test_from_keyfile (Fixture *fixture, - gconstpointer user_data) +test_from_keyfile (void) { GDesktopAppInfo *info; GKeyFile *kf; @@ -681,23 +562,23 @@ main (int argc, char *argv[]) { g_setenv ("XDG_CURRENT_DESKTOP", "GNOME", TRUE); - g_test_init (&argc, &argv, NULL); + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); g_test_bug_base ("https://bugzilla.gnome.org/show_bug.cgi?id="); - g_test_add ("/appinfo/basic", Fixture, NULL, setup, test_basic, teardown); - g_test_add ("/appinfo/text", Fixture, NULL, setup, test_text, teardown); - g_test_add ("/appinfo/launch", Fixture, NULL, setup, test_launch, teardown); - g_test_add ("/appinfo/launch/no-appid", Fixture, NULL, setup, test_launch_no_app_id, teardown); - g_test_add ("/appinfo/show-in", Fixture, NULL, setup, test_show_in, teardown); - g_test_add ("/appinfo/commandline", Fixture, NULL, setup, test_commandline, teardown); - g_test_add ("/appinfo/launch-context", Fixture, NULL, setup, test_launch_context, teardown); - g_test_add ("/appinfo/launch-context-signals", Fixture, NULL, setup, test_launch_context_signals, teardown); - g_test_add ("/appinfo/tryexec", Fixture, NULL, setup, test_tryexec, teardown); - g_test_add ("/appinfo/associations", Fixture, NULL, setup, test_associations, teardown); - g_test_add ("/appinfo/environment", Fixture, NULL, setup, test_environment, teardown); - g_test_add ("/appinfo/startup-wm-class", Fixture, NULL, setup, test_startup_wm_class, teardown); - g_test_add ("/appinfo/supported-types", Fixture, NULL, setup, test_supported_types, teardown); - g_test_add ("/appinfo/from-keyfile", Fixture, NULL, setup, test_from_keyfile, teardown); + g_test_add_func ("/appinfo/basic", test_basic); + g_test_add_func ("/appinfo/text", test_text); + g_test_add_func ("/appinfo/launch", test_launch); + g_test_add_func ("/appinfo/launch/no-appid", test_launch_no_app_id); + g_test_add_func ("/appinfo/show-in", test_show_in); + g_test_add_func ("/appinfo/commandline", test_commandline); + g_test_add_func ("/appinfo/launch-context", test_launch_context); + g_test_add_func ("/appinfo/launch-context-signals", test_launch_context_signals); + g_test_add_func ("/appinfo/tryexec", test_tryexec); + g_test_add_func ("/appinfo/associations", test_associations); + g_test_add_func ("/appinfo/environment", test_environment); + g_test_add_func ("/appinfo/startup-wm-class", test_startup_wm_class); + g_test_add_func ("/appinfo/supported-types", test_supported_types); + g_test_add_func ("/appinfo/from-keyfile", test_from_keyfile); return g_test_run (); } From 914e7c6014bff49fe41f69db0f2517e61289d05c Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 18:03:41 +0000 Subject: [PATCH 25/32] tests: Isolate directories in desktop-app-info test Signed-off-by: Philip Withnall --- gio/tests/desktop-app-info.c | 98 +++--------------------------------- 1 file changed, 8 insertions(+), 90 deletions(-) diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index 176e3961f..bca902d32 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -27,9 +27,7 @@ #include #include -static char *basedir; - -static GAppInfo * +static GAppInfo * create_app_info (const char *name) { GError *error; @@ -66,7 +64,7 @@ test_delete (void) id = g_app_info_get_id (info); g_assert_nonnull (id); - filename = g_build_filename (basedir, "applications", id, NULL); + filename = g_build_filename (g_get_user_data_dir (), "applications", id, NULL); res = g_file_test (filename, G_FILE_TEST_EXISTS); g_assert_true (res); @@ -278,81 +276,6 @@ test_last_used (void) g_object_unref (default_app); } -static gboolean -cleanup_dir_recurse (GFile *parent, - GFile *root, - GError **error) -{ - gboolean ret = FALSE; - GFileEnumerator *enumerator; - GError *local_error = NULL; - - g_assert (root != NULL); - - enumerator = - g_file_enumerate_children (parent, "*", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, - &local_error); - if (!enumerator) - { - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - g_clear_error (&local_error); - ret = TRUE; - } - goto out; - } - - while (TRUE) - { - GFile *child; - GFileInfo *finfo; - char *relative_path; - - if (!g_file_enumerator_iterate (enumerator, &finfo, &child, NULL, error)) - goto out; - if (!finfo) - break; - - relative_path = g_file_get_relative_path (root, child); - g_assert (relative_path != NULL); - g_free (relative_path); - - if (g_file_info_get_file_type (finfo) == G_FILE_TYPE_DIRECTORY) - { - if (!cleanup_dir_recurse (child, root, error)) - goto out; - } - - if (!g_file_delete (child, NULL, error)) - goto out; - } - - ret = TRUE; - out: - g_clear_object (&enumerator); - - return ret; -} - -static void -cleanup_subdirs (const char *base_dir) -{ - GFile *base, *file; - GError *error = NULL; - - base = g_file_new_for_path (base_dir); - file = g_file_get_child (base, "applications"); - (void) cleanup_dir_recurse (file, file, &error); - g_assert_no_error (error); - g_object_unref (file); - file = g_file_get_child (base, "mime"); - (void) cleanup_dir_recurse (file, file, &error); - g_assert_no_error (error); - g_object_unref (file); - g_object_unref (base); -} - static void test_extra_getters (void) { @@ -841,13 +764,12 @@ int main (int argc, char *argv[]) { - gint result; + /* While we use %G_TEST_OPTION_ISOLATE_DIRS to create temporary directories + * for each of the tests, we want to use the system MIME registry, assuming + * that it exists and correctly has shared-mime-info installed. */ + g_content_type_set_mime_dirs (NULL); - g_test_init (&argc, &argv, NULL); - - basedir = g_get_current_dir (); - g_setenv ("XDG_DATA_HOME", basedir, TRUE); - cleanup_subdirs (basedir); + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); g_test_add_func ("/desktop-app-info/delete", test_delete); g_test_add_func ("/desktop-app-info/default", test_default); @@ -860,9 +782,5 @@ main (int argc, g_test_add_func ("/desktop-app-info/show-in", test_show_in); g_test_add_func ("/desktop-app-info/launch-as-manager", test_launch_as_manager); - result = g_test_run (); - - cleanup_subdirs (basedir); - - return result; + return g_test_run (); } From d23c893a0ff5955da856ceed861c625f62a0141e Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 18:09:56 +0000 Subject: [PATCH 26/32] tests: Isolate directories in appmonitor test Signed-off-by: Philip Withnall --- gio/tests/appmonitor.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/gio/tests/appmonitor.c b/gio/tests/appmonitor.c index 30ea108fb..c4e0721ea 100644 --- a/gio/tests/appmonitor.c +++ b/gio/tests/appmonitor.c @@ -3,7 +3,6 @@ typedef struct { - gchar *data_dir; gchar *applications_dir; } Fixture; @@ -11,18 +10,10 @@ static void setup (Fixture *fixture, gconstpointer user_data) { - GError *error = NULL; + fixture->applications_dir = g_build_filename (g_get_user_data_dir (), "applications", NULL); + g_assert_cmpint (g_mkdir_with_parents (fixture->applications_dir, 0755), ==, 0); - fixture->data_dir = g_dir_make_tmp ("gio-test-app-monitor_XXXXXX", &error); - g_assert_no_error (error); - - fixture->applications_dir = g_build_filename (fixture->data_dir, "applications", NULL); - g_assert_cmpint (g_mkdir (fixture->applications_dir, 0755), ==, 0); - - g_setenv ("XDG_DATA_DIRS", fixture->data_dir, TRUE); - g_setenv ("XDG_DATA_HOME", fixture->data_dir, TRUE); - - g_test_message ("Using data directory: %s", fixture->data_dir); + g_test_message ("Using data directory: %s", g_get_user_data_dir ()); } static void @@ -31,9 +22,6 @@ teardown (Fixture *fixture, { g_assert_cmpint (g_rmdir (fixture->applications_dir), ==, 0); g_clear_pointer (&fixture->applications_dir, g_free); - - g_assert_cmpint (g_rmdir (fixture->data_dir), ==, 0); - g_clear_pointer (&fixture->data_dir, g_free); } static gboolean @@ -129,7 +117,7 @@ test_app_monitor (Fixture *fixture, int main (int argc, char *argv[]) { - g_test_init (&argc, &argv, NULL); + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); g_test_add ("/monitor/app", Fixture, NULL, setup, test_app_monitor, teardown); From 707c3f2495d0f03dcb4b52a6b535ded5cae377f1 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 30 Nov 2018 18:16:09 +0000 Subject: [PATCH 27/32] tests: Isolate directories in mimeapps test Signed-off-by: Philip Withnall --- gio/tests/.gitignore | 3 -- gio/tests/Makefile.am | 3 -- gio/tests/mimeapps.c | 107 ++++++++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore index cf724c3de..69e02d573 100644 --- a/gio/tests/.gitignore +++ b/gio/tests/.gitignore @@ -141,9 +141,6 @@ unix-fd unix-streams vfs volumemonitor -xdgconfighome -xdgdatadir -xdgdatahome xgen-gio xgen-giosrc.c gresource-big-test.txt diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 26c2d95b6..a08159065 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -358,9 +358,6 @@ uninstalled_test_extra_programs += \ if !OS_COCOA test_extra_programs += apps test_programs += mimeapps -clean-local: clean-mimeapps -clean-mimeapps: - rm -rf xdgdatadir xdgdatahome xdgconfighome endif appinfo-test-gnome.desktop: appinfo-test-gnome.desktop.in Makefile diff --git a/gio/tests/mimeapps.c b/gio/tests/mimeapps.c index ab6c30fdb..08bcce247 100644 --- a/gio/tests/mimeapps.c +++ b/gio/tests/mimeapps.c @@ -95,18 +95,22 @@ const gchar *mimecache_data = "image/bmp=myapp4.desktop;myapp5.desktop;\n" "image/png=myapp3.desktop;\n"; +typedef struct +{ + gchar *mimeapps_list_home; /* (owned) */ +} Fixture; + /* Set up XDG_DATA_HOME and XDG_DATA_DIRS. * XDG_DATA_DIRS/applications will contain mimeapps.list * XDG_DATA_HOME/applications will contain myapp.desktop * and myapp2.desktop, and no mimeapps.list */ static void -setup (void) +setup (Fixture *fixture, + gconstpointer test_data) { - gchar *dir; - gchar *xdgconfighome; - gchar *xdgdatahome; - gchar *xdgdatadir; + const gchar *xdgdatahome; + const gchar * const *xdgdatadirs; gchar *appdir; gchar *apphome; gchar *mimeapps; @@ -114,18 +118,12 @@ setup (void) gint res; GError *error = NULL; - dir = g_get_current_dir (); - xdgconfighome = g_build_filename (dir, "xdgconfighome", NULL); - xdgdatahome = g_build_filename (dir, "xdgdatahome", NULL); - xdgdatadir = g_build_filename (dir, "xdgdatadir", NULL); - g_test_message ("setting XDG_CONFIG_HOME to '%s'", xdgconfighome); - g_setenv ("XDG_CONFIG_HOME", xdgconfighome, TRUE); - g_test_message ("setting XDG_DATA_HOME to '%s'", xdgdatahome); - g_setenv ("XDG_DATA_HOME", xdgdatahome, TRUE); - g_test_message ("setting XDG_DATA_DIRS to '%s'", xdgdatadir); - g_setenv ("XDG_DATA_DIRS", xdgdatadir, TRUE); + /* These are already set to a temporary directory through our use of + * %G_TEST_OPTION_ISOLATE_DIRS below. */ + xdgdatahome = g_get_user_data_dir (); + xdgdatadirs = g_get_system_data_dirs (); - appdir = g_build_filename (xdgdatadir, "applications", NULL); + appdir = g_build_filename (xdgdatadirs[0], "applications", NULL); g_test_message ("creating '%s'", appdir); res = g_mkdir_with_parents (appdir, 0700); g_assert_cmpint (res, ==, 0); @@ -187,17 +185,24 @@ setup (void) g_assert_no_error (error); g_free (name); - g_free (dir); - g_free (xdgconfighome); - g_free (xdgdatahome); - g_free (xdgdatadir); g_free (apphome); g_free (appdir); g_free (mimeapps); + + /* Pointer to one of the temporary directories. */ + fixture->mimeapps_list_home = g_build_filename (g_get_user_config_dir (), "mimeapps.list", NULL); } static void -test_mime_api (void) +teardown (Fixture *fixture, + gconstpointer test_data) +{ + g_free (fixture->mimeapps_list_home); +} + +static void +test_mime_api (Fixture *fixture, + gconstpointer test_data) { GAppInfo *appinfo; GAppInfo *appinfo2; @@ -286,7 +291,8 @@ test_mime_api (void) * mimeapps.list to verify the results. */ static void -test_mime_file (void) +test_mime_file (Fixture *fixture, + gconstpointer test_data) { gchar **assoc; GAppInfo *appinfo; @@ -297,13 +303,8 @@ test_mime_file (void) gboolean res; GAppInfo *def; GList *list; - gchar *mimeapps; - gchar *dir; const gchar *contenttype = "application/pdf"; - dir = g_get_current_dir (); - mimeapps = g_build_filename (dir, "xdgconfighome", "mimeapps.list", NULL); - /* clear things out */ g_app_info_reset_type_associations (contenttype); @@ -320,7 +321,7 @@ test_mime_file (void) g_assert_no_error (error); keyfile = g_key_file_new (); - g_key_file_load_from_file (keyfile, mimeapps, G_KEY_FILE_NONE, &error); + g_key_file_load_from_file (keyfile, fixture->mimeapps_list_home, G_KEY_FILE_NONE, &error); g_assert_no_error (error); assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); @@ -340,7 +341,7 @@ test_mime_file (void) g_assert_no_error (error); keyfile = g_key_file_new (); - g_key_file_load_from_file (keyfile, mimeapps, G_KEY_FILE_NONE, &error); + g_key_file_load_from_file (keyfile, fixture->mimeapps_list_home, G_KEY_FILE_NONE, &error); g_assert_no_error (error); assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); @@ -359,7 +360,7 @@ test_mime_file (void) g_assert_no_error (error); keyfile = g_key_file_new (); - g_key_file_load_from_file (keyfile, mimeapps, G_KEY_FILE_NONE, &error); + g_key_file_load_from_file (keyfile, fixture->mimeapps_list_home, G_KEY_FILE_NONE, &error); g_assert_no_error (error); assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); @@ -379,7 +380,7 @@ test_mime_file (void) g_assert_no_error (error); keyfile = g_key_file_new (); - g_key_file_load_from_file (keyfile, mimeapps, G_KEY_FILE_NONE, &error); + g_key_file_load_from_file (keyfile, fixture->mimeapps_list_home, G_KEY_FILE_NONE, &error); g_assert_no_error (error); assoc = g_key_file_get_string_list (keyfile, "Added Associations", contenttype, NULL, &error); @@ -393,7 +394,7 @@ test_mime_file (void) g_app_info_reset_type_associations (contenttype); keyfile = g_key_file_new (); - g_key_file_load_from_file (keyfile, mimeapps, G_KEY_FILE_NONE, &error); + g_key_file_load_from_file (keyfile, fixture->mimeapps_list_home, G_KEY_FILE_NONE, &error); g_assert_no_error (error); res = g_key_file_has_key (keyfile, "Added Associations", contenttype, NULL); @@ -406,14 +407,12 @@ test_mime_file (void) g_object_unref (appinfo); g_object_unref (appinfo2); - - g_free (mimeapps); - g_free (dir); } /* test interaction between mimeapps.list at different levels */ static void -test_mime_default (void) +test_mime_default (Fixture *fixture, + gconstpointer test_data) { GAppInfo *appinfo; GAppInfo *appinfo2; @@ -490,7 +489,8 @@ test_mime_default (void) * change the default */ static void -test_mime_default_last_used (void) +test_mime_default_last_used (Fixture *fixture, + gconstpointer test_data) { GAppInfo *appinfo4; GAppInfo *appinfo5; @@ -586,7 +586,8 @@ test_mime_default_last_used (void) } static void -test_scheme_handler (void) +test_scheme_handler (Fixture *fixture, + gconstpointer test_data) { GAppInfo *info, *info5; @@ -601,7 +602,8 @@ test_scheme_handler (void) /* test that g_app_info_* ignores desktop files with nonexisting executables */ static void -test_mime_ignore_nonexisting (void) +test_mime_ignore_nonexisting (Fixture *fixture, + gconstpointer test_data) { GAppInfo *appinfo; @@ -610,7 +612,8 @@ test_mime_ignore_nonexisting (void) } static void -test_all (void) +test_all (Fixture *fixture, + gconstpointer test_data) { GList *all, *l; @@ -625,17 +628,21 @@ test_all (void) int main (int argc, char *argv[]) { - g_test_init (&argc, &argv, NULL); + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); - setup (); - - g_test_add_func ("/appinfo/mime/api", test_mime_api); - g_test_add_func ("/appinfo/mime/default", test_mime_default); - g_test_add_func ("/appinfo/mime/file", test_mime_file); - g_test_add_func ("/appinfo/mime/scheme-handler", test_scheme_handler); - g_test_add_func ("/appinfo/mime/default-last-used", test_mime_default_last_used); - g_test_add_func ("/appinfo/mime/ignore-nonexisting", test_mime_ignore_nonexisting); - g_test_add_func ("/appinfo/all", test_all); + g_test_add ("/appinfo/mime/api", Fixture, NULL, setup, + test_mime_api, teardown); + g_test_add ("/appinfo/mime/default", Fixture, NULL, setup, + test_mime_default, teardown); + g_test_add ("/appinfo/mime/file", Fixture, NULL, setup, + test_mime_file, teardown); + g_test_add ("/appinfo/mime/scheme-handler", Fixture, NULL, setup, + test_scheme_handler, teardown); + g_test_add ("/appinfo/mime/default-last-used", Fixture, NULL, setup, + test_mime_default_last_used, teardown); + g_test_add ("/appinfo/mime/ignore-nonexisting", Fixture, NULL, setup, + test_mime_ignore_nonexisting, teardown); + g_test_add ("/appinfo/all", Fixture, NULL, setup, test_all, teardown); return g_test_run (); } From 60c2533f44cf6b45134fdb1a9bced425f7704ce4 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 14 Dec 2018 12:05:19 +0000 Subject: [PATCH 28/32] tests: Port appinfo test from g_assert() to g_assert_*() Signed-off-by: Philip Withnall --- gio/tests/appinfo.c | 100 +++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/gio/tests/appinfo.c b/gio/tests/appinfo.c index 6e081832f..d21e5fef0 100644 --- a/gio/tests/appinfo.c +++ b/gio/tests/appinfo.c @@ -9,7 +9,8 @@ static void test_launch_for_app_info (GAppInfo *appinfo) { - GError *error; + GError *error = NULL; + gboolean success; GFile *file; GList *l; const gchar *path; @@ -21,20 +22,22 @@ test_launch_for_app_info (GAppInfo *appinfo) return; } - error = NULL; - g_assert (g_app_info_launch (appinfo, NULL, NULL, &error)); + success = g_app_info_launch (appinfo, NULL, NULL, &error); g_assert_no_error (error); + g_assert_true (success); - g_assert (g_app_info_launch_uris (appinfo, NULL, NULL, &error)); + success = g_app_info_launch_uris (appinfo, NULL, NULL, &error); g_assert_no_error (error); + g_assert_true (success); path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); file = g_file_new_for_path (path); l = NULL; l = g_list_append (l, file); - g_assert (g_app_info_launch (appinfo, l, NULL, &error)); + success = g_app_info_launch (appinfo, l, NULL, &error); g_assert_no_error (error); + g_assert_true (success); g_list_free (l); g_object_unref (file); @@ -43,8 +46,10 @@ test_launch_for_app_info (GAppInfo *appinfo) l = g_list_append (l, uri); l = g_list_append (l, "file:///etc/group#adm"); - g_assert (g_app_info_launch_uris (appinfo, l, NULL, &error)); + success = g_app_info_launch_uris (appinfo, l, NULL, &error); g_assert_no_error (error); + g_assert_true (success); + g_list_free (l); g_free (uri); } @@ -57,7 +62,7 @@ test_launch (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); - g_assert (appinfo != NULL); + g_assert_nonnull (appinfo); test_launch_for_app_info (appinfo); g_object_unref (appinfo); @@ -117,7 +122,7 @@ test_launch_no_app_id (void) g_assert_true (loaded); appinfo = (GAppInfo*)g_desktop_app_info_new_from_keyfile (fake_desktop_file); - g_assert (appinfo != NULL); + g_assert_nonnull (appinfo); test_launch_for_app_info (appinfo); @@ -189,14 +194,15 @@ test_basic (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); + g_assert_nonnull (appinfo); g_assert_cmpstr (g_app_info_get_id (appinfo), ==, "appinfo-test.desktop"); - g_assert (strstr (g_app_info_get_executable (appinfo), "appinfo-test") != NULL); + g_assert_nonnull (strstr (g_app_info_get_executable (appinfo), "appinfo-test")); icon = g_app_info_get_icon (appinfo); - g_assert (G_IS_THEMED_ICON (icon)); + g_assert_true (G_IS_THEMED_ICON (icon)); icon2 = g_themed_icon_new ("testicon"); - g_assert (g_icon_equal (icon, icon2)); + g_assert_true (g_icon_equal (icon, icon2)); g_object_unref (icon2); appinfo2 = g_app_info_dup (appinfo); @@ -215,17 +221,17 @@ test_show_in (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); - g_assert (g_app_info_should_show (appinfo)); + g_assert_true (g_app_info_should_show (appinfo)); g_object_unref (appinfo); path = g_test_get_filename (G_TEST_BUILT, "appinfo-test-gnome.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); - g_assert (g_app_info_should_show (appinfo)); + g_assert_true (g_app_info_should_show (appinfo)); g_object_unref (appinfo); path = g_test_get_filename (G_TEST_BUILT, "appinfo-test-notgnome.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); - g_assert (!g_app_info_should_show (appinfo)); + g_assert_false (g_app_info_should_show (appinfo)); g_object_unref (appinfo); } @@ -245,12 +251,12 @@ test_commandline (void) "cmdline-app-test", G_APP_INFO_CREATE_SUPPORTS_URIS, &error); - g_assert (appinfo != NULL); g_assert_no_error (error); + g_assert_nonnull (appinfo); g_assert_cmpstr (g_app_info_get_name (appinfo), ==, "cmdline-app-test"); g_assert_cmpstr (g_app_info_get_commandline (appinfo), ==, cmdline_out); - g_assert (g_app_info_supports_uris (appinfo)); - g_assert (!g_app_info_supports_files (appinfo)); + g_assert_true (g_app_info_supports_uris (appinfo)); + g_assert_false (g_app_info_supports_files (appinfo)); g_object_unref (appinfo); @@ -262,12 +268,12 @@ test_commandline (void) "cmdline-app-test", G_APP_INFO_CREATE_NONE, &error); - g_assert (appinfo != NULL); g_assert_no_error (error); + g_assert_nonnull (appinfo); g_assert_cmpstr (g_app_info_get_name (appinfo), ==, "cmdline-app-test"); g_assert_cmpstr (g_app_info_get_commandline (appinfo), ==, cmdline_out); - g_assert (!g_app_info_supports_uris (appinfo)); - g_assert (g_app_info_supports_files (appinfo)); + g_assert_false (g_app_info_supports_uris (appinfo)); + g_assert_true (g_app_info_supports_files (appinfo)); g_object_unref (appinfo); @@ -292,10 +298,10 @@ test_launch_context (void) NULL); str = g_app_launch_context_get_display (context, appinfo, NULL); - g_assert (str == NULL); + g_assert_null (str); str = g_app_launch_context_get_startup_notify_id (context, appinfo, NULL); - g_assert (str == NULL); + g_assert_null (str); g_object_unref (appinfo); g_object_unref (context); @@ -314,8 +320,8 @@ launched (GAppLaunchContext *context, gint pid; pid = 0; - g_assert (g_variant_lookup (platform_data, "pid", "i", &pid)); - g_assert (pid != 0); + g_assert_true (g_variant_lookup (platform_data, "pid", "i", &pid)); + g_assert_cmpint (pid, !=, 0); launched_reached = TRUE; } @@ -333,6 +339,7 @@ test_launch_context_signals (void) GAppLaunchContext *context; GAppInfo *appinfo; GError *error = NULL; + gboolean success; gchar *cmdline; cmdline = g_strconcat (g_test_get_dir (G_TEST_BUILT), "/appinfo-test --option", NULL); @@ -345,11 +352,11 @@ test_launch_context_signals (void) G_APP_INFO_CREATE_SUPPORTS_URIS, NULL); - error = NULL; - g_assert (g_app_info_launch (appinfo, NULL, context, &error)); + success = g_app_info_launch (appinfo, NULL, context, &error); g_assert_no_error (error); + g_assert_true (success); - g_assert (launched_reached); + g_assert_true (launched_reached); g_object_unref (appinfo); g_object_unref (context); @@ -366,7 +373,7 @@ test_tryexec (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test2.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); - g_assert (appinfo == NULL); + g_assert_null (appinfo); } /* Test that we can set an appinfo as default for a mime type or @@ -377,7 +384,7 @@ test_associations (void) { GAppInfo *appinfo; GAppInfo *appinfo2; - GError *error; + GError *error = NULL; gboolean result; GList *list; gchar *cmdline; @@ -389,33 +396,31 @@ test_associations (void) NULL); g_free (cmdline); - error = NULL; result = g_app_info_set_as_default_for_type (appinfo, "application/x-glib-test", &error); - - g_assert (result); g_assert_no_error (error); + g_assert_true (result); appinfo2 = g_app_info_get_default_for_type ("application/x-glib-test", FALSE); - g_assert (appinfo2); + g_assert_nonnull (appinfo2); g_assert_cmpstr (g_app_info_get_commandline (appinfo), ==, g_app_info_get_commandline (appinfo2)); g_object_unref (appinfo2); result = g_app_info_set_as_default_for_extension (appinfo, "gio-tests", &error); - g_assert (result); g_assert_no_error (error); + g_assert_true (result); appinfo2 = g_app_info_get_default_for_type ("application/x-extension-gio-tests", FALSE); - g_assert (appinfo2); + g_assert_nonnull (appinfo2); g_assert_cmpstr (g_app_info_get_commandline (appinfo), ==, g_app_info_get_commandline (appinfo2)); g_object_unref (appinfo2); result = g_app_info_add_supports_type (appinfo, "application/x-gio-test", &error); - g_assert (result); g_assert_no_error (error); + g_assert_true (result); list = g_app_info_get_all_for_type ("application/x-gio-test"); g_assert_cmpint (g_list_length (list), ==, 1); @@ -424,12 +429,13 @@ test_associations (void) g_object_unref (appinfo2); g_list_free (list); - g_assert (g_app_info_can_remove_supports_type (appinfo)); - g_assert (g_app_info_remove_supports_type (appinfo, "application/x-gio-test", &error)); + g_assert_true (g_app_info_can_remove_supports_type (appinfo)); + result = g_app_info_remove_supports_type (appinfo, "application/x-gio-test", &error); g_assert_no_error (error); + g_assert_true (result); - g_assert (g_app_info_can_delete (appinfo)); - g_assert (g_app_info_delete (appinfo)); + g_assert_true (g_app_info_can_delete (appinfo)); + g_assert_true (g_app_info_delete (appinfo)); g_object_unref (appinfo); } @@ -448,8 +454,8 @@ test_environment (void) env = g_app_launch_context_get_environment (ctx); - g_assert (g_environ_getenv (env, "FOO") == NULL); - g_assert (g_environ_getenv (env, "BLA") == NULL); + g_assert_null (g_environ_getenv (env, "FOO")); + g_assert_null (g_environ_getenv (env, "BLA")); g_assert_cmpstr (g_environ_getenv (env, "PATH"), ==, path); g_strfreev (env); @@ -471,7 +477,7 @@ test_environment (void) env = g_app_launch_context_get_environment (ctx); g_assert_cmpstr (g_environ_getenv (env, "FOO"), ==, "baz"); - g_assert (g_environ_getenv (env, "BLA") == NULL); + g_assert_null (g_environ_getenv (env, "BLA")); g_strfreev (env); @@ -531,13 +537,13 @@ test_from_keyfile (void) g_assert_no_error (error); info = g_desktop_app_info_new_from_keyfile (kf); g_key_file_unref (kf); - g_assert (info != NULL); + g_assert_nonnull (info); g_object_get (info, "filename", &file, NULL); - g_assert (file == NULL); + g_assert_null (file); file = g_desktop_app_info_get_filename (info); - g_assert (file == NULL); + g_assert_null (file); categories = g_desktop_app_info_get_categories (info); g_assert_cmpstr (categories, ==, "GNOME;GTK;"); categories_list = g_desktop_app_info_get_string_list (info, "Categories", &categories_count); @@ -551,7 +557,7 @@ test_from_keyfile (void) g_assert_cmpstr (keywords[1], ==, "test keyword"); name = g_desktop_app_info_get_generic_name (info); g_assert_cmpstr (name, ==, "generic-appinfo-test"); - g_assert (!g_desktop_app_info_get_nodisplay (info)); + g_assert_false (g_desktop_app_info_get_nodisplay (info)); g_strfreev (categories_list); g_object_unref (info); From 1ea4ba8d6ee90af7b6fd682b3b82931bbeccf6cd Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 14 Dec 2018 13:05:17 +0000 Subject: [PATCH 29/32] tests: Use static appinfo .desktop file when not launching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The appinfo-test.desktop file is set up with an Exec= path which points to the compiled and installed appinfo-test utility. When running the tests uninstalled, however, this might not be present, which causes loading appinfo-test.desktop to fail. Split appinfo-test.desktop in two: keep the existing appinfo-test.desktop for tests which need to launch appinfo-test, and add a new appinfo-test-static.desktop for tests which don’t launch anything (and, for example, just inspect GAppInfo properties). appinfo-test-static.desktop uses an Exec= line which should always be present (`true`) so it should never fail to load. Allow the tests using appinfo-test-static.desktop to be run uninstalled or installed. Allow the tests using appinfo-test.desktop to be skipped if loading appinfo-test.desktop fails, which is an indicator that the test is running uninstalled. Signed-off-by: Philip Withnall --- gio/tests/Makefile.am | 1 + gio/tests/appinfo-test-static.desktop | 19 ++++++++++++++++++ gio/tests/appinfo.c | 28 +++++++++++++++++++-------- gio/tests/desktop-app-info.c | 9 +++++++-- gio/tests/meson.build | 1 + 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 gio/tests/appinfo-test-static.desktop diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index a08159065..87e8dae7c 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -339,6 +339,7 @@ dist_test_data += \ dist_test_data += \ appinfo-test-actions.desktop \ + appinfo-test-static.desktop \ file.c \ org.gtk.test.dbusappinfo.desktop \ x-content/image-dcf/DCIM/Camera/20130831_203925.jpg \ diff --git a/gio/tests/appinfo-test-static.desktop b/gio/tests/appinfo-test-static.desktop new file mode 100644 index 000000000..f8a09f642 --- /dev/null +++ b/gio/tests/appinfo-test-static.desktop @@ -0,0 +1,19 @@ +[Desktop Entry] +Type=Application +GenericName=generic-appinfo-test +Name=appinfo-test +Name[de]=appinfo-test-de +X-GNOME-FullName=example +X-GNOME-FullName[de]=Beispiel +Comment=GAppInfo example +Comment[de]=GAppInfo Beispiel +Exec=true --option %U %i --name %c --filename %k %m %% +Icon=testicon.svg +Terminal=true +StartupNotify=true +StartupWMClass=appinfo-class +MimeType=image/png;image/jpeg; +Keywords=keyword1;test keyword; +Categories=GNOME;GTK; +X-JunkFood=Burger +X-JunkFood[de]=Bratwurst \ No newline at end of file diff --git a/gio/tests/appinfo.c b/gio/tests/appinfo.c index d21e5fef0..f13698d70 100644 --- a/gio/tests/appinfo.c +++ b/gio/tests/appinfo.c @@ -62,7 +62,12 @@ test_launch (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); - g_assert_nonnull (appinfo); + + if (appinfo == NULL) + { + g_test_skip ("appinfo-test binary not installed"); + return; + } test_launch_for_app_info (appinfo); g_object_unref (appinfo); @@ -146,7 +151,7 @@ test_locale (const char *locale) g_setenv ("LANGUAGE", locale, TRUE); setlocale (LC_ALL, ""); - path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); if (g_strcmp0 (locale, "C") == 0) @@ -192,12 +197,12 @@ test_basic (void) GIcon *icon, *icon2; const gchar *path; - path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); g_assert_nonnull (appinfo); - g_assert_cmpstr (g_app_info_get_id (appinfo), ==, "appinfo-test.desktop"); - g_assert_nonnull (strstr (g_app_info_get_executable (appinfo), "appinfo-test")); + g_assert_cmpstr (g_app_info_get_id (appinfo), ==, "appinfo-test-static.desktop"); + g_assert_nonnull (strstr (g_app_info_get_executable (appinfo), "true")); icon = g_app_info_get_icon (appinfo); g_assert_true (G_IS_THEMED_ICON (icon)); @@ -221,6 +226,13 @@ test_show_in (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (path); + + if (appinfo == NULL) + { + g_test_skip ("appinfo-test binary not installed"); + return; + } + g_assert_true (g_app_info_should_show (appinfo)); g_object_unref (appinfo); @@ -491,7 +503,7 @@ test_startup_wm_class (void) const char *wm_class; const gchar *path; - path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL); appinfo = g_desktop_app_info_new_from_filename (path); wm_class = g_desktop_app_info_get_startup_wm_class (appinfo); @@ -507,7 +519,7 @@ test_supported_types (void) const char * const *content_types; const gchar *path; - path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL); appinfo = G_APP_INFO (g_desktop_app_info_new_from_filename (path)); content_types = g_app_info_get_supported_types (appinfo); @@ -531,7 +543,7 @@ test_from_keyfile (void) const gchar *name; const gchar *path; - path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); + path = g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL); kf = g_key_file_new (); g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, &error); g_assert_no_error (error); diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index bca902d32..10ba85338 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -288,7 +288,7 @@ test_extra_getters (void) g_setenv ("LANGUAGE", "de_DE.UTF8", TRUE); setlocale (LC_ALL, ""); - appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL)); + appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL)); g_assert_nonnull (appinfo); g_assert_true (g_desktop_app_info_has_key (appinfo, "Terminal")); @@ -739,7 +739,12 @@ test_launch_as_manager (void) path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = g_desktop_app_info_new_from_filename (path); - g_assert_nonnull (appinfo); + + if (appinfo == NULL) + { + g_test_skip ("appinfo-test binary not installed"); + return; + } retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, NULL, 0, NULL, NULL, diff --git a/gio/tests/meson.build b/gio/tests/meson.build index a07ddf807..c39d89803 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -424,6 +424,7 @@ if installed_tests_enabled 'contexts.c', 'g-icon.c', 'appinfo-test-actions.desktop', + 'appinfo-test-static.desktop', 'file.c', 'org.gtk.test.dbusappinfo.desktop', install_dir : installed_tests_execdir, From f8421059e96486c468eb268f891906e40db9955b Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 14 Dec 2018 13:43:45 +0000 Subject: [PATCH 30/32] tests: Add some debug output to desktop-app-info test Signed-off-by: Philip Withnall --- gio/tests/desktop-app-info.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index 10ba85338..e4010b8ce 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -408,6 +408,7 @@ run_apps (const gchar *command, gchar **argv; gint status; gchar *out; + gchar *argv_str = NULL; argv = g_new (gchar *, 4); argv[0] = g_test_build_filename (G_TEST_BUILT, "apps", NULL); @@ -454,6 +455,10 @@ run_apps (const gchar *command, g_assert_true (success); g_assert_cmpuint (status, ==, 0); + argv_str = g_strjoinv (" ", argv); + g_test_message ("%s: `%s` returned: %s", G_STRFUNC, argv_str, out); + g_free (argv_str); + g_strfreev (envp); g_strfreev (argv); From 1947834b701400eb806f7edfb12600c367dfeac2 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 14 Dec 2018 13:44:09 +0000 Subject: [PATCH 31/32] tests: Disable debug output from desktop-app-info subprocess The `apps` subprocess is spawned by desktop-app-info to interpret the forest of .desktop files, and its output is provided on stdout. If debug output is mixed up with that output, tests which parse the output fail. Disable the debug output from the subprocess to prevent this. The new debug output appeared as a result of recent changes to the desktop file dir monitoring code in gdesktopappinfo.c. Signed-off-by: Philip Withnall --- gio/tests/desktop-app-info.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index e4010b8ce..dd41af842 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -451,6 +451,8 @@ run_apps (const gchar *command, else envp = g_environ_unsetenv (envp, "XDG_CURRENT_DESKTOP"); + envp = g_environ_setenv (envp, "G_MESSAGES_DEBUG", "", TRUE); + success = g_spawn_sync (NULL, argv, envp, 0, NULL, NULL, &out, NULL, &status, NULL); g_assert_true (success); g_assert_cmpuint (status, ==, 0); From 7a79984068170aec9a34b7060e345d9cbe5e0159 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 14 Dec 2018 18:03:05 +0000 Subject: [PATCH 32/32] gutils: Drop fallback handling for NULL homedirs in XDG getters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s not possible for g_build_home_dir() to return NULL. The fallback code here seems to originate from commit 1607e3f1 in 2005 (bug 169348), where it was added with the explanation “Guard against g_home_dir being NULL”. The XDG Base Directory specification doesn’t have anything to say about what to do when $HOME is unset: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html It’s all a bit moot, though, becaause since commit 9cbfb560 (bug 773435), g_{get,build}_home_dir() cannot return NULL. So just drop the fallback. See discussion on https://gitlab.gnome.org/GNOME/glib/merge_requests/505#note_386109. Signed-off-by: Philip Withnall --- glib/gutils.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/glib/gutils.c b/glib/gutils.c index 2fcccc33d..09c1523a7 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -1292,12 +1292,7 @@ g_build_user_data_dir (void) if (!data_dir || !data_dir[0]) { gchar *home_dir = g_build_home_dir (); - - if (home_dir != NULL) - data_dir = g_build_filename (home_dir, ".local", "share", NULL); - else - data_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".local", "share", NULL); - + data_dir = g_build_filename (home_dir, ".local", "share", NULL); g_free (home_dir); } @@ -1357,12 +1352,7 @@ g_build_user_config_dir (void) if (!config_dir || !config_dir[0]) { gchar *home_dir = g_build_home_dir (); - - if (home_dir != NULL) - config_dir = g_build_filename (home_dir, ".config", NULL); - else - config_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".config", NULL); - + config_dir = g_build_filename (home_dir, ".config", NULL); g_free (home_dir); } @@ -1422,12 +1412,7 @@ g_build_user_cache_dir (void) if (!cache_dir || !cache_dir[0]) { gchar *home_dir = g_build_home_dir (); - - if (home_dir != NULL) - cache_dir = g_build_filename (home_dir, ".cache", NULL); - else - cache_dir = g_build_filename (g_get_tmp_dir (), g_get_user_name (), ".cache", NULL); - + cache_dir = g_build_filename (home_dir, ".cache", NULL); g_free (home_dir); }