diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 709c229ca..22b48d9ef 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -1690,6 +1690,7 @@ void (*argv)[0], "G_TEST_TMPDIR"); exit (1); } + _g_unset_cached_tmp_dir (); /* And clear the traditional environment variables so subprocesses * spawned by the code under test can’t trash anything. If a test diff --git a/glib/gutils.c b/glib/gutils.c index 186ef7d07..d73b6474b 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -598,6 +598,7 @@ static gchar *g_user_state_dir = NULL; static gchar *g_user_runtime_dir = NULL; static gchar **g_system_config_dirs = NULL; static gchar **g_user_special_dirs = NULL; +static gchar *g_tmp_dir = NULL; /* fifteen minutes of fame for everybody */ #define G_USER_DIRS_EXPIRE 15 * 60 @@ -941,6 +942,17 @@ g_get_home_dir (void) return home_dir; } +void +_g_unset_cached_tmp_dir (void) +{ + G_LOCK (g_utils_global); + /* We have to leak the old value, as user code could be retaining pointers + * to it. */ + g_ignore_leak (g_tmp_dir); + g_tmp_dir = NULL; + G_UNLOCK (g_utils_global); +} + /** * g_get_tmp_dir: * @@ -964,22 +976,33 @@ g_get_home_dir (void) const gchar * g_get_tmp_dir (void) { - static gchar *tmp_dir; + G_LOCK (g_utils_global); - if (g_once_init_enter (&tmp_dir)) + if (g_tmp_dir == NULL) { gchar *tmp; -#ifdef G_OS_WIN32 - tmp = g_strdup (g_getenv ("TEMP")); + tmp = g_strdup (g_getenv ("G_TEST_TMPDIR")); + if (tmp == NULL || *tmp == '\0') + { + g_free (tmp); + tmp = g_strdup (g_getenv ( +#ifdef G_OS_WIN32 + "TEMP" +#else /* G_OS_WIN32 */ + "TMPDIR" +#endif /* G_OS_WIN32 */ + )); + } + +#ifdef G_OS_WIN32 if (tmp == NULL || *tmp == '\0') { g_free (tmp); tmp = get_windows_directory_root (); } #else /* G_OS_WIN32 */ - tmp = g_strdup (g_getenv ("TMPDIR")); #ifdef P_tmpdir if (tmp == NULL || *tmp == '\0') @@ -1000,10 +1023,12 @@ g_get_tmp_dir (void) } #endif /* !G_OS_WIN32 */ - g_once_init_leave (&tmp_dir, tmp); + g_tmp_dir = g_steal_pointer (&tmp); } - return tmp_dir; + G_UNLOCK (g_utils_global); + + return g_tmp_dir; } /** diff --git a/glib/gutilsprivate.h b/glib/gutilsprivate.h index f7d435d61..0d9b0df14 100644 --- a/glib/gutilsprivate.h +++ b/glib/gutilsprivate.h @@ -59,6 +59,8 @@ g_nearest_pow (gsize num) return n + 1; } +void _g_unset_cached_tmp_dir (void); + G_END_DECLS #endif /* __G_UTILS_PRIVATE_H__ */ diff --git a/glib/tests/fileutils.c b/glib/tests/fileutils.c index 8ca25f9cc..b9149720d 100644 --- a/glib/tests/fileutils.c +++ b/glib/tests/fileutils.c @@ -1205,6 +1205,7 @@ test_dir_make_tmp (void) name = g_dir_make_tmp ("testXXXXXXtest", &error); g_assert_no_error (error); g_assert_true (g_file_test (name, G_FILE_TEST_IS_DIR)); + g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR"))); ret = g_rmdir (name); g_assert_cmpint (ret, ==, 0); g_free (name); @@ -1212,6 +1213,7 @@ test_dir_make_tmp (void) name = g_dir_make_tmp (NULL, &error); g_assert_no_error (error); g_assert_true (g_file_test (name, G_FILE_TEST_IS_DIR)); + g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR"))); ret = g_rmdir (name); g_assert_cmpint (ret, ==, 0); g_free (name); @@ -1238,6 +1240,7 @@ test_file_open_tmp (void) g_assert_cmpint (fd, !=, -1); g_assert_no_error (error); g_assert_nonnull (name); + g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR"))); unlink (name); g_free (name); close (fd); @@ -1246,6 +1249,7 @@ test_file_open_tmp (void) g_assert_cmpint (fd, !=, -1); g_assert_no_error (error); g_assert_nonnull (name); + g_assert_true (g_str_has_prefix (name, g_getenv ("G_TEST_TMPDIR"))); g_unlink (name); g_free (name); close (fd); diff --git a/glib/tests/meson.build b/glib/tests/meson.build index a01649b77..cec91a49a 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -158,6 +158,7 @@ glib_tests = { 'utils' : { 'c_standards': c_standards.keys(), }, + 'utils-isolated' : {}, 'unicode' : {}, 'unicode-encoding' : {}, 'unicode-normalize': {}, diff --git a/glib/tests/utils-isolated.c b/glib/tests/utils-isolated.c new file mode 100644 index 000000000..6ffb3424f --- /dev/null +++ b/glib/tests/utils-isolated.c @@ -0,0 +1,114 @@ +/* Copyright (C) 2022 Marco Trevisan + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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: Marco Trevisan + */ + +#include "config.h" + +#include + +/* Test that all of the well-known directories returned by GLib + * are returned as children of test_tmpdir when running with + * %G_TEST_OPTION_ISOLATE_DIRS. This ensures that tests should + * not interfere with each other in `/tmp` while running. + */ + +const char *test_tmpdir; + +static void +test_tmp_dir (void) +{ + g_assert_cmpstr (g_get_tmp_dir (), ==, test_tmpdir); +} + +static void +test_home_dir (void) +{ + g_assert_true (g_str_has_prefix (g_get_home_dir (), test_tmpdir)); +} + +static void +test_user_cache_dir (void) +{ + g_assert_true (g_str_has_prefix (g_get_user_cache_dir (), test_tmpdir)); +} + +static void +test_system_config_dirs (void) +{ + const char *const *dir; + + for (dir = g_get_system_config_dirs (); *dir != NULL; dir++) + g_assert_true (g_str_has_prefix (*dir, test_tmpdir)); +} + +static void +test_user_config_dir (void) +{ + g_assert_true (g_str_has_prefix (g_get_user_config_dir (), test_tmpdir)); +} + +static void +test_system_data_dirs (void) +{ + const char *const *dir; + + for (dir = g_get_system_data_dirs (); *dir != NULL; dir++) + g_assert_true (g_str_has_prefix (*dir, test_tmpdir)); +} + +static void +test_user_data_dir (void) +{ + g_assert_true (g_str_has_prefix (g_get_user_data_dir (), test_tmpdir)); +} + +static void +test_user_state_dir (void) +{ + g_assert_true (g_str_has_prefix (g_get_user_state_dir (), test_tmpdir)); +} + +static void +test_user_runtime_dir (void) +{ + g_assert_true (g_str_has_prefix (g_get_user_runtime_dir (), test_tmpdir)); +} + + +int +main (int argc, + char *argv[]) +{ + g_setenv ("LC_ALL", "C", TRUE); + g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); + + test_tmpdir = g_getenv ("G_TEST_TMPDIR"); + g_assert_nonnull (test_tmpdir); + + g_test_add_func ("/utils-isolated/tmp-dir", test_tmp_dir); + g_test_add_func ("/utils-isolated/home-dir", test_home_dir); + g_test_add_func ("/utils-isolated/user-cache-dir", test_user_cache_dir); + g_test_add_func ("/utils-isolated/system-config-dirs", test_system_config_dirs); + g_test_add_func ("/utils-isolated/user-config-dir", test_user_config_dir); + g_test_add_func ("/utils-isolated/system-data-dirs", test_system_data_dirs); + g_test_add_func ("/utils-isolated/user-data-dir", test_user_data_dir); + g_test_add_func ("/utils-isolated/user-state-dir", test_user_state_dir); + g_test_add_func ("/utils-isolated/user-runtime-dir", test_user_runtime_dir); + return g_test_run (); +} diff --git a/tools/glib.supp b/tools/glib.supp index 0609db44e..5b03557d9 100644 --- a/tools/glib.supp +++ b/tools/glib.supp @@ -840,6 +840,19 @@ fun:g_set_user_dirs } +# _g_unset_cached_tmp_dir() deliberately leaks the previous cached g_get_tmp_dir() values. +# These will not all be reachable on exit. +{ + g_get_tmp_dir + Memcheck:Leak + match-leak-kinds:definite,reachable + fun:malloc + ... + fun:g_get_tmp_dir + ... + fun:g_test_init +} + # g_get_system_data_dirs() caches a one-time allocation { g_get_system_data_dirs