/* Unit tests for utilities * Copyright (C) 2010 Red Hat, Inc. * * This work is provided "as is"; redistribution and modification * in whole or in part, in any medium, physical or electronic is * permitted without restriction. * * This work 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. * * In no event shall the authors or contributors be liable for any * direct, indirect, incidental, special, exemplary, or consequential * damages (including, but not limited to, procurement of substitute * goods or services; loss of use, data, or profits; or business * interruption) however caused and on any theory of liability, whether * in contract, strict liability, or tort (including negligence or * otherwise) arising in any way out of the use of this software, even * if advised of the possibility of such damage. * * Author: Matthias Clasen */ #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS #define GLIB_DISABLE_DEPRECATION_WARNINGS #endif #include "glib.h" #include "glib-private.h" #include #include #include #ifdef G_OS_UNIX #include #endif #ifdef G_OS_WIN32 #include #endif static gboolean strv_check (const gchar * const *strv, ...) { va_list args; gchar *s; gint i; va_start (args, strv); for (i = 0; strv[i]; i++) { s = va_arg (args, gchar*); if (g_strcmp0 (strv[i], s) != 0) { va_end (args); return FALSE; } } va_end (args); return TRUE; } static void test_language_names (void) { const gchar * const *names; g_setenv ("LANGUAGE", "de:en_US", TRUE); names = g_get_language_names (); g_assert (strv_check (names, "de", "en_US", "en", "C", NULL)); g_setenv ("LANGUAGE", "tt_RU.UTF-8@iqtelif", TRUE); names = g_get_language_names (); g_assert (strv_check (names, "tt_RU.UTF-8@iqtelif", "tt_RU@iqtelif", "tt.UTF-8@iqtelif", "tt@iqtelif", "tt_RU.UTF-8", "tt_RU", "tt.UTF-8", "tt", "C", NULL)); } static void test_locale_variants (void) { char **v; v = g_get_locale_variants ("fr_BE"); g_assert (strv_check ((const gchar * const *) v, "fr_BE", "fr", NULL)); g_strfreev (v); v = g_get_locale_variants ("sr_SR@latin"); g_assert (strv_check ((const gchar * const *) v, "sr_SR@latin", "sr@latin", "sr_SR", "sr", NULL)); g_strfreev (v); } static void test_version (void) { if (g_test_verbose ()) g_printerr ("(header %d.%d.%d library %d.%d.%d) ", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION, glib_major_version, glib_minor_version, glib_micro_version); g_assert (glib_check_version (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION) == NULL); g_assert (glib_check_version (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, 0) == NULL); g_assert (glib_check_version (GLIB_MAJOR_VERSION - 1, 0, 0) != NULL); g_assert (glib_check_version (GLIB_MAJOR_VERSION + 1, 0, 0) != NULL); g_assert (glib_check_version (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION + 1, 0) != NULL); /* don't use + 1 here, since a +/-1 difference can * happen due to post-release version bumps in git */ g_assert (glib_check_version (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION + 3) != NULL); } static const gchar *argv0; static void test_appname (void) { const gchar *prgname; const gchar *appname; prgname = g_get_prgname (); appname = g_get_application_name (); g_assert_cmpstr (prgname, ==, argv0); g_assert_cmpstr (appname, ==, prgname); g_set_prgname ("prgname"); prgname = g_get_prgname (); appname = g_get_application_name (); g_assert_cmpstr (prgname, ==, "prgname"); g_assert_cmpstr (appname, ==, "prgname"); g_set_application_name ("appname"); prgname = g_get_prgname (); appname = g_get_application_name (); g_assert_cmpstr (prgname, ==, "prgname"); g_assert_cmpstr (appname, ==, "appname"); } static void test_tmpdir (void) { g_test_bug ("627969"); g_assert_cmpstr (g_get_tmp_dir (), !=, ""); } static void test_bits (void) { gulong mask; gint max_bit; gint i, pos; pos = g_bit_nth_lsf (0, -1); g_assert_cmpint (pos, ==, -1); max_bit = sizeof (gulong) * 8; for (i = 0; i < max_bit; i++) { mask = 1UL << i; pos = g_bit_nth_lsf (mask, -1); g_assert_cmpint (pos, ==, i); pos = g_bit_nth_lsf (mask, i - 3); g_assert_cmpint (pos , ==, i); pos = g_bit_nth_lsf (mask, i); g_assert_cmpint (pos , ==, -1); pos = g_bit_nth_lsf (mask, i + 1); g_assert_cmpint (pos , ==, -1); } pos = g_bit_nth_msf (0, -1); g_assert_cmpint (pos, ==, -1); for (i = 0; i < max_bit; i++) { mask = 1UL << i; pos = g_bit_nth_msf (mask, -1); g_assert_cmpint (pos, ==, i); pos = g_bit_nth_msf (mask, i + 3); g_assert_cmpint (pos , ==, i); pos = g_bit_nth_msf (mask, i); g_assert_cmpint (pos , ==, -1); if (i > 0) { pos = g_bit_nth_msf (mask, i - 1); g_assert_cmpint (pos , ==, -1); } } } static void test_swap (void) { guint16 a16, b16; guint32 a32, b32; guint64 a64, b64; a16 = 0xaabb; b16 = 0xbbaa; g_assert_cmpint (GUINT16_SWAP_LE_BE (a16), ==, b16); a32 = 0xaaaabbbb; b32 = 0xbbbbaaaa; g_assert_cmpint (GUINT32_SWAP_LE_BE (a32), ==, b32); a64 = G_GUINT64_CONSTANT(0xaaaaaaaabbbbbbbb); b64 = G_GUINT64_CONSTANT(0xbbbbbbbbaaaaaaaa); g_assert_cmpint (GUINT64_SWAP_LE_BE (a64), ==, b64); } static void test_find_program (void) { gchar *res; #ifdef G_OS_UNIX res = g_find_program_in_path ("sh"); g_assert (res != NULL); g_free (res); res = g_find_program_in_path ("/bin/sh"); g_assert (res != NULL); g_free (res); #else /* There's not a lot we can search for that would reliably work both * on real Windows and mingw. */ #endif res = g_find_program_in_path ("this_program_does_not_exit"); g_assert (res == NULL); res = g_find_program_in_path ("/bin"); g_assert (res == NULL); res = g_find_program_in_path ("/etc/passwd"); g_assert (res == NULL); } static void test_debug (void) { GDebugKey keys[] = { { "key1", 1 }, { "key2", 2 }, { "key3", 4 }, }; guint res; res = g_parse_debug_string (NULL, keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 0); res = g_parse_debug_string ("foobabla;#!%!$%112 223", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 0); res = g_parse_debug_string ("key1:key2", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 3); res = g_parse_debug_string ("key1;key2", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 3); res = g_parse_debug_string ("key1,key2", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 3); res = g_parse_debug_string ("key1 key2", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 3); res = g_parse_debug_string ("key1\tkey2", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 3); res = g_parse_debug_string ("all", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 7); if (g_test_subprocess ()) { res = g_parse_debug_string ("help", keys, G_N_ELEMENTS (keys)); g_assert_cmpint (res, ==, 0); return; } g_test_trap_subprocess (NULL, 0, 0); g_test_trap_assert_passed (); g_test_trap_assert_stderr ("*Supported debug values: key1 key2 key3 all help*"); } static void test_codeset (void) { gchar *c; const gchar *c2; c = g_get_codeset (); g_get_charset (&c2); g_assert_cmpstr (c, ==, c2); g_free (c); } static void test_codeset2 (void) { if (g_test_subprocess ()) { const gchar *c; g_setenv ("CHARSET", "UTF-8", TRUE); g_get_charset (&c); g_assert_cmpstr (c, ==, "UTF-8"); return; } g_test_trap_subprocess (NULL, 0, 0); g_test_trap_assert_passed (); } static void test_console_charset (void) { const gchar *c1; const gchar *c2; #ifdef G_OS_WIN32 /* store current environment and unset $LANG to make sure it does not interfere */ const unsigned int initial_cp = GetConsoleOutputCP (); gchar *initial_lang = g_strdup (g_getenv ("LANG")); g_unsetenv ("LANG"); /* set console output codepage to something specific (ISO-8859-1 aka CP28591) and query it */ SetConsoleOutputCP (28591); g_get_console_charset (&c1); g_assert_cmpstr (c1, ==, "ISO-8859-1"); /* set $LANG to something specific (should override the console output codepage) and query it */ g_setenv ("LANG", "de_DE.ISO-8859-15@euro", TRUE); g_get_console_charset (&c2); g_assert_cmpstr (c2, ==, "ISO-8859-15"); /* reset environment */ if (initial_cp) SetConsoleOutputCP (initial_cp); if (initial_lang) g_setenv ("LANG", initial_lang, TRUE); g_free (initial_lang); #else g_get_charset (&c1); g_get_console_charset (&c2); g_assert_cmpstr (c1, ==, c2); #endif } static void test_basename (void) { const gchar *path = "/path/to/a/file/deep/down.sh"; const gchar *b; b = g_basename (path); g_assert_cmpstr (b, ==, "down.sh"); } extern const gchar *glib_pgettext (const gchar *msgidctxt, gsize msgidoffset); static void test_gettext (void) { const gchar *am0, *am1, *am2, *am3; am0 = glib_pgettext ("GDateTime\004AM", strlen ("GDateTime") + 1); am1 = g_dpgettext ("glib20", "GDateTime\004AM", strlen ("GDateTime") + 1); am2 = g_dpgettext ("glib20", "GDateTime|AM", 0); am3 = g_dpgettext2 ("glib20", "GDateTime", "AM"); g_assert_cmpstr (am0, ==, am1); g_assert_cmpstr (am1, ==, am2); g_assert_cmpstr (am2, ==, am3); } static void test_username (void) { const gchar *name; name = g_get_user_name (); g_assert (name != NULL); } static void test_realname (void) { const gchar *name; name = g_get_real_name (); g_assert (name != NULL); } static void test_hostname (void) { const gchar *name; name = g_get_host_name (); g_assert (name != NULL); g_assert_true (g_utf8_validate (name, -1, NULL)); } #ifdef G_OS_UNIX static void test_xdg_dirs (void) { gchar *xdg; const gchar *dir; const gchar * const *dirs; gchar *s; xdg = g_strdup (g_getenv ("XDG_CONFIG_HOME")); if (!xdg) xdg = g_build_filename (g_get_home_dir (), ".config", NULL); dir = g_get_user_config_dir (); g_assert_cmpstr (dir, ==, xdg); g_free (xdg); xdg = g_strdup (g_getenv ("XDG_DATA_HOME")); if (!xdg) xdg = g_build_filename (g_get_home_dir (), ".local", "share", NULL); dir = g_get_user_data_dir (); g_assert_cmpstr (dir, ==, xdg); g_free (xdg); xdg = g_strdup (g_getenv ("XDG_CACHE_HOME")); if (!xdg) xdg = g_build_filename (g_get_home_dir (), ".cache", NULL); dir = g_get_user_cache_dir (); g_assert_cmpstr (dir, ==, xdg); g_free (xdg); xdg = g_strdup (g_getenv ("XDG_RUNTIME_DIR")); if (!xdg) xdg = g_strdup (g_get_user_cache_dir ()); dir = g_get_user_runtime_dir (); g_assert_cmpstr (dir, ==, xdg); g_free (xdg); xdg = (gchar *)g_getenv ("XDG_CONFIG_DIRS"); if (!xdg) xdg = "/etc/xdg"; dirs = g_get_system_config_dirs (); s = g_strjoinv (":", (gchar **)dirs); g_assert_cmpstr (s, ==, xdg); g_free (s); } #endif static void test_special_dir (void) { const gchar *dir, *dir2; dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); g_reload_user_special_dirs_cache (); dir2 = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); g_assert_cmpstr (dir, ==, dir2); } static void test_desktop_special_dir (void) { const gchar *dir, *dir2; dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); g_assert (dir != NULL); g_reload_user_special_dirs_cache (); dir2 = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); g_assert (dir2 != NULL); } static void test_os_info (void) { gchar *name; gchar *contents = NULL; #ifdef G_OS_UNIX struct utsname info; #endif /* Whether this is implemented or not, it must not crash */ name = g_get_os_info (G_OS_INFO_KEY_NAME); g_test_message ("%s: %s", G_OS_INFO_KEY_NAME, name == NULL ? "(null)" : name); #if defined (G_OS_WIN32) || defined (__APPLE__) /* These OSs have a special case so NAME should always succeed */ g_assert_nonnull (name); #elif defined (G_OS_UNIX) if (g_file_get_contents ("/etc/os-release", &contents, NULL, NULL) || g_file_get_contents ("/usr/lib/os-release", &contents, NULL, NULL) || uname (&info) == 0) g_assert_nonnull (name); else g_test_skip ("os-release(5) API not implemented on this platform"); #else g_test_skip ("g_get_os_info() not supported on this platform"); #endif g_free (name); g_free (contents); } static gboolean source_test (gpointer data) { g_assert_not_reached (); return G_SOURCE_REMOVE; } static void test_clear_source (void) { guint id; id = g_idle_add (source_test, NULL); g_assert_cmpuint (id, >, 0); g_clear_handle_id (&id, g_source_remove); g_assert_cmpuint (id, ==, 0); id = g_timeout_add (100, source_test, NULL); g_assert_cmpuint (id, >, 0); g_clear_handle_id (&id, g_source_remove); g_assert_cmpuint (id, ==, 0); } static void test_clear_pointer (void) { gpointer a; a = g_malloc (5); g_clear_pointer (&a, g_free); g_assert (a == NULL); a = g_malloc (5); (g_clear_pointer) (&a, g_free); g_assert (a == NULL); } /* Test that g_clear_pointer() works with a GDestroyNotify which contains a cast. * See https://gitlab.gnome.org/GNOME/glib/issues/1425 */ static void test_clear_pointer_cast (void) { GHashTable *hash_table = NULL; hash_table = g_hash_table_new (g_str_hash, g_str_equal); g_assert_nonnull (hash_table); g_clear_pointer (&hash_table, (void (*) (GHashTable *)) g_hash_table_destroy); g_assert_null (hash_table); } /* Test that the macro version of g_clear_pointer() only evaluates its argument * once, just like the function version would. */ static void test_clear_pointer_side_effects (void) { gchar **my_string_array, **i; my_string_array = g_new0 (gchar*, 3); my_string_array[0] = g_strdup ("hello"); my_string_array[1] = g_strdup ("there"); my_string_array[2] = NULL; i = my_string_array; g_clear_pointer (i++, g_free); g_assert_true (i == &my_string_array[1]); g_assert_null (my_string_array[0]); g_assert_nonnull (my_string_array[1]); g_assert_null (my_string_array[2]); g_free (my_string_array[1]); g_free (my_string_array[2]); g_free (my_string_array); } static int obj_count; static void get_obj (gpointer *obj_out) { gpointer obj = g_malloc (5); obj_count++; if (obj_out) *obj_out = g_steal_pointer (&obj); if (obj) { g_free (obj); obj_count--; } } static void test_take_pointer (void) { gpointer a; gpointer b; get_obj (NULL); get_obj (&a); g_assert (a); /* ensure that it works to skip the macro */ b = (g_steal_pointer) (&a); g_assert (!a); obj_count--; g_free (b); g_assert (!obj_count); } static void test_misc_mem (void) { gpointer a; a = g_try_malloc (0); g_assert (a == NULL); a = g_try_malloc0 (0); g_assert (a == NULL); a = g_malloc (16); a = g_try_realloc (a, 20); a = g_try_realloc (a, 0); g_assert (a == NULL); } static void test_nullify (void) { gpointer p = &test_nullify; g_assert (p != NULL); g_nullify_pointer (&p); g_assert (p == NULL); } static void atexit_func (void) { g_print ("atexit called"); } static void test_atexit (void) { if (g_test_subprocess ()) { g_atexit (atexit_func); return; } g_test_trap_subprocess (NULL, 0, 0); g_test_trap_assert_passed (); g_test_trap_assert_stdout ("*atexit called*"); } static void test_check_setuid (void) { gboolean res; res = GLIB_PRIVATE_CALL(g_check_setuid) (); g_assert (!res); } /* Test the defined integer limits are correct, as some compilers have had * problems with signed/unsigned conversion in the past. These limits should not * vary between platforms, compilers or architectures. * * Use string comparisons to avoid the same systematic problems with unary minus * application in C++. See https://gitlab.gnome.org/GNOME/glib/issues/1663. */ static void test_int_limits (void) { gchar *str = NULL; g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1663"); str = g_strdup_printf ("%d %d %u\n" "%" G_GINT16_FORMAT " %" G_GINT16_FORMAT " %" G_GUINT16_FORMAT "\n" "%" G_GINT32_FORMAT " %" G_GINT32_FORMAT " %" G_GUINT32_FORMAT "\n" "%" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GUINT64_FORMAT "\n", G_MININT8, G_MAXINT8, G_MAXUINT8, G_MININT16, G_MAXINT16, G_MAXUINT16, G_MININT32, G_MAXINT32, G_MAXUINT32, G_MININT64, G_MAXINT64, G_MAXUINT64); g_assert_cmpstr (str, ==, "-128 127 255\n" "-32768 32767 65535\n" "-2147483648 2147483647 4294967295\n" "-9223372036854775808 9223372036854775807 18446744073709551615\n"); g_free (str); } static void test_clear_list (void) { GList *list = NULL; g_clear_list (&list, NULL); g_assert_null (list); list = g_list_prepend (list, "test"); g_assert_nonnull (list); g_clear_list (&list, NULL); g_assert_null (list); g_clear_list (&list, g_free); g_assert_null (list); list = g_list_prepend (list, g_malloc (16)); g_assert_nonnull (list); g_clear_list (&list, g_free); g_assert_null (list); } static void test_clear_slist (void) { GSList *slist = NULL; g_clear_slist (&slist, NULL); g_assert_null (slist); slist = g_slist_prepend (slist, "test"); g_assert_nonnull (slist); g_clear_slist (&slist, NULL); g_assert_null (slist); g_clear_slist (&slist, g_free); g_assert_null (slist); slist = g_slist_prepend (slist, g_malloc (16)); g_assert_nonnull (slist); g_clear_slist (&slist, g_free); g_assert_null (slist); } int main (int argc, char *argv[]) { argv0 = argv[0]; /* for tmpdir test, need to do this early before g_get_any_init */ g_setenv ("TMPDIR", "", TRUE); g_unsetenv ("TMP"); g_unsetenv ("TEMP"); /* g_test_init() only calls g_set_prgname() if g_get_prgname() * returns %NULL, but g_get_prgname() on Windows never returns NULL. * So we need to do this by hand to make test_appname() work on * Windows. */ g_set_prgname (argv[0]); g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/"); g_test_add_func ("/utils/language-names", test_language_names); g_test_add_func ("/utils/locale-variants", test_locale_variants); g_test_add_func ("/utils/version", test_version); g_test_add_func ("/utils/appname", test_appname); g_test_add_func ("/utils/tmpdir", test_tmpdir); g_test_add_func ("/utils/bits", test_bits); g_test_add_func ("/utils/swap", test_swap); g_test_add_func ("/utils/find-program", test_find_program); g_test_add_func ("/utils/debug", test_debug); g_test_add_func ("/utils/codeset", test_codeset); g_test_add_func ("/utils/codeset2", test_codeset2); g_test_add_func ("/utils/console-charset", test_console_charset); g_test_add_func ("/utils/basename", test_basename); g_test_add_func ("/utils/gettext", test_gettext); g_test_add_func ("/utils/username", test_username); g_test_add_func ("/utils/realname", test_realname); g_test_add_func ("/utils/hostname", test_hostname); #ifdef G_OS_UNIX g_test_add_func ("/utils/xdgdirs", test_xdg_dirs); #endif g_test_add_func ("/utils/specialdir", test_special_dir); g_test_add_func ("/utils/specialdir/desktop", test_desktop_special_dir); g_test_add_func ("/utils/os-info", test_os_info); g_test_add_func ("/utils/clear-pointer", test_clear_pointer); g_test_add_func ("/utils/clear-pointer-cast", test_clear_pointer_cast); g_test_add_func ("/utils/clear-pointer/side-effects", test_clear_pointer_side_effects); g_test_add_func ("/utils/take-pointer", test_take_pointer); g_test_add_func ("/utils/clear-source", test_clear_source); g_test_add_func ("/utils/misc-mem", test_misc_mem); g_test_add_func ("/utils/nullify", test_nullify); g_test_add_func ("/utils/atexit", test_atexit); g_test_add_func ("/utils/check-setuid", test_check_setuid); g_test_add_func ("/utils/int-limits", test_int_limits); g_test_add_func ("/utils/clear-list", test_clear_list); g_test_add_func ("/utils/clear-slist", test_clear_slist); return g_test_run (); }