/* * Copyright (C) 2008 Red Hat, Inc * * 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: Matthias Clasen */ #include #include #include #include #include #include #include #include #include #include static GAppInfo * create_command_line_app_info (const char *name, const char *command_line, const char *default_for_type) { GAppInfo *info; GError *error = NULL; info = g_app_info_create_from_commandline (command_line, name, G_APP_INFO_CREATE_NONE, &error); g_assert_no_error (error); g_app_info_set_as_default_for_type (info, default_for_type, &error); g_assert_no_error (error); return g_steal_pointer (&info); } static GAppInfo * create_app_info (const char *name) { GError *error = NULL; GAppInfo *info; info = create_command_line_app_info (name, "true blah", "application/x-blah"); /* this is necessary to ensure that the info is saved */ g_app_info_remove_supports_type (info, "application/x-blah", &error); g_assert_no_error (error); g_app_info_reset_type_associations ("application/x-blah"); return info; } static void test_delete (void) { GAppInfo *info; const char *id; char *filename; gboolean res; info = create_app_info ("Blah"); id = g_app_info_get_id (info); g_assert_nonnull (id); 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); res = g_app_info_can_delete (info); g_assert_true (res); res = g_app_info_delete (info); g_assert_true (res); res = g_file_test (filename, G_FILE_TEST_EXISTS); 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_nonnull (info); res = g_app_info_can_delete (info); g_assert_false (res); res = g_app_info_delete (info); g_assert_false (res); } g_free (filename); } static void test_default (void) { GAppInfo *info, *info1, *info2, *info3; GList *list; GError *error = NULL; info1 = create_app_info ("Blah1"); info2 = create_app_info ("Blah2"); info3 = create_app_info ("Blah3"); g_app_info_set_as_default_for_type (info1, "application/x-test", &error); g_assert_no_error (error); g_app_info_set_as_default_for_type (info2, "application/x-test", &error); g_assert_no_error (error); info = g_app_info_get_default_for_type ("application/x-test", FALSE); g_assert_nonnull (info); g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); g_object_unref (info); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*uri_scheme*failed*"); g_assert_null (g_app_info_get_default_for_uri_scheme (NULL)); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*uri_scheme*failed*"); g_assert_null (g_app_info_get_default_for_uri_scheme ("")); g_test_assert_expected_messages (); g_app_info_set_as_default_for_type (info3, "x-scheme-handler/glib", &error); g_assert_no_error (error); info = g_app_info_get_default_for_uri_scheme ("glib"); g_assert_nonnull (info); g_assert_true (g_app_info_equal (info, info3)); 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_no_error (error); /* check that info2 is still default */ info = g_app_info_get_default_for_type ("application/x-test", FALSE); 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_no_error (error); /* and make sure info2 is still default */ info = g_app_info_get_default_for_type ("application/x-test", FALSE); g_assert_nonnull (info); g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); g_object_unref (info); /* now clean it all up */ g_app_info_reset_type_associations ("application/x-test"); g_app_info_reset_type_associations ("x-scheme-handler/glib"); list = g_app_info_get_all_for_type ("application/x-test"); g_assert_null (list); list = g_app_info_get_all_for_type ("x-scheme-handler/glib"); g_assert_null (list); g_app_info_delete (info1); g_app_info_delete (info2); g_app_info_delete (info3); g_object_unref (info1); g_object_unref (info2); g_object_unref (info3); } typedef struct { GAppInfo *expected_info; GMainLoop *loop; } DefaultForTypeData; static void ensure_default_type_result (GAppInfo *info, DefaultForTypeData *data, GError *error) { if (data->expected_info) { g_assert_nonnull (info); g_assert_no_error (error); g_assert_true (g_app_info_equal (info, data->expected_info)); } else { g_assert_null (info); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); } g_main_loop_quit (data->loop); g_clear_object (&info); g_clear_error (&error); } static void on_default_for_type_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GAppInfo *info; GError *error = NULL; DefaultForTypeData *data = user_data; g_assert_null (object); info = g_app_info_get_default_for_type_finish (result, &error); ensure_default_type_result (info, data, error); } static void on_default_for_uri_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GAppInfo *info; GError *error = NULL; DefaultForTypeData *data = user_data; g_assert_null (object); info = g_app_info_get_default_for_uri_scheme_finish (result, &error); ensure_default_type_result (info, data, error); } static void test_default_async (void) { DefaultForTypeData data; GAppInfo *info1, *info2, *info3; GList *list; GError *error = NULL; data.loop = g_main_loop_new (NULL, TRUE); info1 = create_app_info ("Blah1"); info2 = create_app_info ("Blah2"); info3 = create_app_info ("Blah3"); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*content_type*failed*"); g_app_info_get_default_for_type_async (NULL, FALSE, NULL, NULL, NULL); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*content_type*failed*"); g_app_info_get_default_for_type_async ("", FALSE, NULL, NULL, NULL); g_test_assert_expected_messages (); g_app_info_set_as_default_for_type (info1, "application/x-test", &error); g_assert_no_error (error); g_app_info_set_as_default_for_type (info2, "application/x-test", &error); g_assert_no_error (error); data.expected_info = info2; g_app_info_get_default_for_type_async ("application/x-test", FALSE, NULL, on_default_for_type_cb, &data); g_main_loop_run (data.loop); /* now try adding something, but not setting as default */ g_app_info_add_supports_type (info3, "application/x-test", &error); g_assert_no_error (error); /* check that info2 is still default */ data.expected_info = info2; g_app_info_get_default_for_type_async ("application/x-test", FALSE, NULL, on_default_for_type_cb, &data); g_main_loop_run (data.loop); /* now remove info1 again */ g_app_info_remove_supports_type (info1, "application/x-test", &error); g_assert_no_error (error); /* and make sure info2 is still default */ data.expected_info = info2; g_app_info_get_default_for_type_async ("application/x-test", FALSE, NULL, on_default_for_type_cb, &data); g_main_loop_run (data.loop); g_app_info_set_as_default_for_type (info3, "x-scheme-handler/glib-async", &error); g_assert_no_error (error); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*uri_scheme*failed*"); g_assert_null (g_app_info_get_default_for_uri_scheme (NULL)); g_test_assert_expected_messages (); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*uri_scheme*failed*"); g_assert_null (g_app_info_get_default_for_uri_scheme ("")); g_test_assert_expected_messages (); data.expected_info = info3; g_app_info_get_default_for_uri_scheme_async ("glib-async", NULL, on_default_for_uri_cb, &data); g_main_loop_run (data.loop); /* now clean it all up */ g_app_info_reset_type_associations ("application/x-test"); data.expected_info = NULL; g_app_info_get_default_for_type_async ("application/x-test", FALSE, NULL, on_default_for_type_cb, &data); g_main_loop_run (data.loop); g_app_info_reset_type_associations ("x-scheme-handler/glib-async"); data.expected_info = NULL; g_app_info_get_default_for_uri_scheme_async ("glib-async", NULL, on_default_for_uri_cb, &data); g_main_loop_run (data.loop); list = g_app_info_get_all_for_type ("application/x-test"); g_assert_null (list); g_app_info_delete (info1); g_app_info_delete (info2); g_app_info_delete (info3); g_object_unref (info1); g_object_unref (info2); g_object_unref (info3); g_main_loop_unref (data.loop); } static void test_fallback (void) { GAppInfo *info1, *info2, *app = NULL; GList *apps, *recomm, *fallback, *list, *l, *m; GError *error = NULL; gint old_length; info1 = create_app_info ("Test1"); info2 = create_app_info ("Test2"); 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_no_error (error); g_app_info_add_supports_type (info2, "text/plain", &error); g_assert_no_error (error); /* check that both apps are registered */ apps = g_app_info_get_all_for_type ("text/x-python"); g_assert_cmpint (g_list_length (apps), ==, old_length + 2); /* check that Test1 is among the recommended apps */ recomm = g_app_info_get_recommended_for_type ("text/x-python"); g_assert_nonnull (recomm); for (l = recomm; l; l = l->next) { app = l->data; if (g_app_info_equal (info1, app)) break; } g_assert_nonnull (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_nonnull (fallback); for (l = fallback; l; l = l->next) { app = l->data; if (g_app_info_equal (info2, app)) break; } g_assert_cmpstr (g_app_info_get_name (app), ==, "Test2"); /* check that recomm + fallback = all applications */ list = g_list_concat (g_list_copy (recomm), g_list_copy (fallback)); 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_true (g_app_info_equal (l->data, m->data)); } g_list_free (list); g_list_free_full (apps, g_object_unref); g_list_free_full (recomm, g_object_unref); g_list_free_full (fallback, g_object_unref); g_app_info_reset_type_associations ("text/x-python"); g_app_info_reset_type_associations ("text/plain"); g_app_info_delete (info1); g_app_info_delete (info2); g_object_unref (info1); g_object_unref (info2); } static void test_last_used (void) { GList *applications; GAppInfo *info1, *info2, *default_app; GError *error = NULL; info1 = create_app_info ("Test1"); info2 = create_app_info ("Test2"); g_app_info_set_as_default_for_type (info1, "application/x-test", &error); g_assert_no_error (error); g_app_info_add_supports_type (info2, "application/x-test", &error); g_assert_no_error (error); applications = g_app_info_get_recommended_for_type ("application/x-test"); g_assert_cmpuint (g_list_length (applications), ==, 2); /* the first should be the default app now */ 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_no_error (error); applications = g_app_info_get_recommended_for_type ("application/x-test"); g_assert_cmpuint (g_list_length (applications), ==, 2); default_app = g_app_info_get_default_for_type ("application/x-test", FALSE); g_assert_true (g_app_info_equal (default_app, info1)); /* the first should be the other app now */ 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); g_app_info_reset_type_associations ("application/x-test"); g_app_info_delete (info1); g_app_info_delete (info2); g_object_unref (info1); g_object_unref (info2); g_object_unref (default_app); } static void test_extra_getters (void) { GDesktopAppInfo *appinfo; const gchar *lang; gchar *s; gboolean b; lang = setlocale (LC_ALL, NULL); 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-static.desktop", NULL)); g_assert_nonnull (appinfo); 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"); g_free (s); s = g_desktop_app_info_get_locale_string (appinfo, "X-JunkFood"); g_assert_cmpstr (s, ==, "Bratwurst"); g_free (s); g_setenv ("LANGUAGE", "sv_SE.UTF8", TRUE); setlocale (LC_ALL, ""); s = g_desktop_app_info_get_locale_string (appinfo, "X-JunkFood"); g_assert_cmpstr (s, ==, "Burger"); /* fallback */ g_free (s); b = g_desktop_app_info_get_boolean (appinfo, "Terminal"); g_assert_true (b); g_object_unref (appinfo); g_setenv ("LANGUAGE", lang, TRUE); setlocale (LC_ALL, ""); } static void wait_for_file (const gchar *want_this, const gchar *but_not_this, const gchar *or_this) { 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 * be OK. * * 600 * 100ms = 60 seconds. */ while (access (want_this, F_OK) != 0) { g_usleep (100000); /* 100ms */ g_assert_cmpuint (retries, >, 0); retries--; } 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); unlink (or_this); } static void test_actions (void) { const char *expected[] = { "frob", "tweak", "twiddle", "broken", NULL }; const gchar * const *actions; GDesktopAppInfo *appinfo; const gchar *tmpdir; gchar *name; gchar *frob_path; gchar *tweak_path; gchar *twiddle_path; appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "appinfo-test-actions.desktop", NULL)); g_assert_nonnull (appinfo); actions = g_desktop_app_info_list_actions (appinfo); g_assert_cmpstrv (actions, expected); name = g_desktop_app_info_get_action_name (appinfo, "frob"); g_assert_cmpstr (name, ==, "Frobnicate"); g_free (name); name = g_desktop_app_info_get_action_name (appinfo, "tweak"); g_assert_cmpstr (name, ==, "Tweak"); g_free (name); name = g_desktop_app_info_get_action_name (appinfo, "twiddle"); g_assert_cmpstr (name, ==, "Twiddle"); g_free (name); name = g_desktop_app_info_get_action_name (appinfo, "broken"); g_assert_nonnull (name); g_assert_true (g_utf8_validate (name, -1, NULL)); g_free (name); tmpdir = g_getenv ("G_TEST_TMPDIR"); g_assert_nonnull (tmpdir); frob_path = g_build_filename (tmpdir, "frob", NULL); tweak_path = g_build_filename (tmpdir, "tweak", NULL); twiddle_path = g_build_filename (tmpdir, "twiddle", NULL); g_assert_false (g_file_test (frob_path, G_FILE_TEST_EXISTS)); g_assert_false (g_file_test (tweak_path, G_FILE_TEST_EXISTS)); g_assert_false (g_file_test (twiddle_path, G_FILE_TEST_EXISTS)); g_desktop_app_info_launch_action (appinfo, "frob", NULL); wait_for_file (frob_path, tweak_path, twiddle_path); g_desktop_app_info_launch_action (appinfo, "tweak", NULL); wait_for_file (tweak_path, frob_path, twiddle_path); g_desktop_app_info_launch_action (appinfo, "twiddle", NULL); wait_for_file (twiddle_path, frob_path, tweak_path); g_free (frob_path); g_free (tweak_path); g_free (twiddle_path); g_object_unref (appinfo); } static gchar * run_apps (const gchar *command, const gchar *arg, gboolean with_usr, gboolean with_home, const gchar *locale_name, const gchar *language, const gchar *xdg_current_desktop) { gboolean success; gchar **envp; 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); argv[1] = g_strdup (command); argv[2] = g_strdup (arg); argv[3] = NULL; envp = g_get_environ (); if (with_usr) { gchar *tmp = g_test_build_filename (G_TEST_DIST, "desktop-files", "usr", NULL); envp = g_environ_setenv (envp, "XDG_DATA_DIRS", tmp, TRUE); g_free (tmp); } else envp = g_environ_setenv (envp, "XDG_DATA_DIRS", "/does-not-exist", TRUE); if (with_home) { gchar *tmp = g_test_build_filename (G_TEST_DIST, "desktop-files", "home", NULL); envp = g_environ_setenv (envp, "XDG_DATA_HOME", tmp, TRUE); g_free (tmp); } else envp = g_environ_setenv (envp, "XDG_DATA_HOME", "/does-not-exist", TRUE); if (locale_name) envp = g_environ_setenv (envp, "LC_ALL", locale_name, TRUE); else envp = g_environ_setenv (envp, "LC_ALL", "C", TRUE); if (language) envp = g_environ_setenv (envp, "LANGUAGE", language, TRUE); else envp = g_environ_unsetenv (envp, "LANGUAGE"); if (xdg_current_desktop) envp = g_environ_setenv (envp, "XDG_CURRENT_DESKTOP", xdg_current_desktop, TRUE); 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); 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); return out; } static void assert_strings_equivalent (const gchar *expected, const gchar *result) { gchar **expected_words; gchar **result_words; gint i, j; expected_words = g_strsplit (expected, " ", 0); result_words = g_strsplit_set (result, " \n", 0); for (i = 0; expected_words[i]; i++) { for (j = 0; result_words[j]; j++) if (g_str_equal (expected_words[i], result_words[j])) goto got_it; g_test_fail_printf ("Unable to find expected string '%s' in result '%s'", expected_words[i], result); got_it: continue; } g_assert_cmpint (g_strv_length (expected_words), ==, g_strv_length (result_words)); g_strfreev (expected_words); g_strfreev (result_words); } static void assert_list (const gchar *expected, gboolean with_usr, gboolean with_home, const gchar *locale_name, const gchar *language) { gchar *result; result = run_apps ("list", NULL, with_usr, with_home, locale_name, language, NULL); g_strchomp (result); assert_strings_equivalent (expected, result); g_free (result); } static void assert_info (const gchar *desktop_id, const gchar *expected, gboolean with_usr, gboolean with_home, const gchar *locale_name, const gchar *language) { gchar *result; result = run_apps ("show-info", desktop_id, with_usr, with_home, locale_name, language, NULL); g_assert_cmpstr (result, ==, expected); g_free (result); } static void assert_search (const gchar *search_string, const gchar *expected, gboolean with_usr, gboolean with_home, const gchar *locale_name, const gchar *language) { gchar **expected_lines; gchar **result_lines; gchar *result; gint i; expected_lines = g_strsplit (expected, "\n", -1); result = run_apps ("search", search_string, with_usr, with_home, locale_name, language, NULL); result_lines = g_strsplit (result, "\n", -1); g_assert_cmpint (g_strv_length (expected_lines), ==, g_strv_length (result_lines)); for (i = 0; expected_lines[i]; i++) assert_strings_equivalent (expected_lines[i], result_lines[i]); g_strfreev (expected_lines); g_strfreev (result_lines); g_free (result); } static void assert_implementations (const gchar *interface, const gchar *expected, gboolean with_usr, gboolean with_home) { gchar *result; result = run_apps ("implementations", interface, with_usr, with_home, NULL, NULL, NULL); g_strchomp (result); assert_strings_equivalent (expected, result); g_free (result); } #define ALL_USR_APPS "evince-previewer.desktop nautilus-classic.desktop gnome-font-viewer.desktop " \ "baobab.desktop yelp.desktop eog.desktop cheese.desktop org.gnome.clocks.desktop " \ "gnome-contacts.desktop kde4-kate.desktop gcr-prompter.desktop totem.desktop " \ "gnome-terminal.desktop nautilus-autorun-software.desktop gcr-viewer.desktop " \ "nautilus-connect-server.desktop kde4-dolphin.desktop gnome-music.desktop " \ "kde4-konqbrowser.desktop gucharmap.desktop kde4-okular.desktop nautilus.desktop " \ "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop " \ "invalid-desktop.desktop" #define HOME_APPS "epiphany-weather-for-toronto-island-9c6a4e022b17686306243dada811d550d25eb1fb.desktop" #define ALL_HOME_APPS HOME_APPS " eog.desktop" static void test_search (void) { assert_list ("", FALSE, FALSE, NULL, NULL); assert_list (ALL_USR_APPS, TRUE, FALSE, NULL, NULL); assert_list (ALL_HOME_APPS, FALSE, TRUE, NULL, NULL); assert_list (ALL_USR_APPS " " HOME_APPS, TRUE, TRUE, NULL, NULL); /* The user has "installed" their own version of eog.desktop which * calls it "Eye of GNOME". Do some testing based on that. * * We should always find "Pictures" keyword no matter where we look. */ assert_search ("Picture", "eog.desktop\n", TRUE, TRUE, NULL, NULL); assert_search ("Picture", "eog.desktop\n", TRUE, FALSE, NULL, NULL); assert_search ("Picture", "eog.desktop\n", FALSE, TRUE, NULL, NULL); assert_search ("Picture", "", FALSE, FALSE, NULL, NULL); /* We should only find it called "eye of gnome" when using the user's * directory. */ assert_search ("eye gnome", "", TRUE, FALSE, NULL, NULL); assert_search ("eye gnome", "eog.desktop\n", FALSE, TRUE, NULL, NULL); assert_search ("eye gnome", "eog.desktop\n", TRUE, TRUE, NULL, NULL); /* We should only find it called "image viewer" when _not_ using the * user's directory. */ assert_search ("image viewer", "eog.desktop\n", TRUE, FALSE, NULL, NULL); assert_search ("image viewer", "", FALSE, TRUE, NULL, NULL); assert_search ("image viewer", "", TRUE, TRUE, NULL, NULL); /* There're "flatpak" apps (clocks) installed as well - they should *not* * match the prefix command ("/bin/sh") in the Exec= line though. */ assert_search ("sh", "gnome-terminal.desktop\n", TRUE, FALSE, NULL, NULL); /* "frobnicator.desktop" is ignored by get_all() because the binary is * missing, but search should still find it (to avoid either stale results * from the cache or expensive stat() calls for each potential result) */ assert_search ("frobni", "frobnicator.desktop\n", TRUE, FALSE, NULL, NULL); /* Obvious multi-word search */ assert_search ("gno hel", "yelp.desktop\n", TRUE, TRUE, NULL, NULL); /* Repeated search terms should do nothing... */ assert_search ("files file fil fi f", "nautilus.desktop\n" "gedit.desktop\n", TRUE, TRUE, NULL, NULL); /* "con" will match "connect" and "contacts" on name but dconf only on * the "config" keyword */ assert_search ("con", "nautilus-connect-server.desktop gnome-contacts.desktop\n" "dconf-editor.desktop\n", TRUE, TRUE, NULL, NULL); /* "gnome" will match "eye of gnome" from the user's directory, plus * matching "GNOME Clocks" X-GNOME-FullName. It's only a comment on * yelp and gnome-contacts, though. */ assert_search ("gnome", "eog.desktop\n" "org.gnome.clocks.desktop\n" "yelp.desktop gnome-contacts.desktop\n", TRUE, TRUE, NULL, NULL); /* eog has exec name 'false' in usr only */ assert_search ("false", "eog.desktop\n", TRUE, FALSE, NULL, NULL); assert_search ("false", "", FALSE, TRUE, NULL, NULL); assert_search ("false", "", TRUE, TRUE, NULL, NULL); assert_search ("false", "", FALSE, FALSE, NULL, NULL); /* make sure we only search the first component */ assert_search ("nonsearchable", "", TRUE, FALSE, NULL, NULL); /* "gnome con" will match only gnome contacts; via the name for * "contacts" and the comment for "gnome" */ assert_search ("gnome con", "gnome-contacts.desktop\n", TRUE, TRUE, NULL, NULL); /* make sure we get the correct kde4- prefix on the application IDs * from subdirectories */ assert_search ("konq", "kde4-konqbrowser.desktop\n", TRUE, TRUE, NULL, NULL); assert_search ("kate", "kde4-kate.desktop\n", TRUE, TRUE, NULL, NULL); /* make sure we can look up apps by name properly */ assert_info ("kde4-kate.desktop", "kde4-kate.desktop\n" "Kate\n" "Kate\n" "nil\n", TRUE, TRUE, NULL, NULL); assert_info ("nautilus.desktop", "nautilus.desktop\n" "Files\n" "Files\n" "Access and organize files\n", TRUE, TRUE, NULL, NULL); /* make sure localised searching works properly */ assert_search ("foliumi", "nautilus.desktop\n" "kde4-konqbrowser.desktop\n" "eog.desktop\n", TRUE, FALSE, "en_US.UTF-8", "eo"); /* the user's eog.desktop has no translations... */ assert_search ("foliumi", "nautilus.desktop\n" "kde4-konqbrowser.desktop\n", TRUE, TRUE, "en_US.UTF-8", "eo"); } static void test_implements (void) { /* Make sure we can find our search providers... */ assert_implementations ("org.gnome.Shell.SearchProvider2", "gnome-music.desktop gnome-contacts.desktop eog.desktop", TRUE, FALSE); /* And our image acquisition possibilities... */ assert_implementations ("org.freedesktop.ImageProvider", "cheese.desktop", TRUE, FALSE); /* Make sure the user's eog is properly masking the system one */ assert_implementations ("org.gnome.Shell.SearchProvider2", "gnome-music.desktop gnome-contacts.desktop", TRUE, TRUE); /* Make sure we get nothing if we have nothing */ assert_implementations ("org.gnome.Shell.SearchProvider2", "", FALSE, FALSE); } static void assert_shown (const gchar *desktop_id, gboolean expected, const gchar *xdg_current_desktop) { gchar *result; result = run_apps ("should-show", desktop_id, TRUE, TRUE, NULL, NULL, xdg_current_desktop); g_assert_cmpstr (result, ==, expected ? "true\n" : "false\n"); g_free (result); } static void test_show_in (void) { assert_shown ("gcr-prompter.desktop", FALSE, NULL); assert_shown ("gcr-prompter.desktop", FALSE, "GNOME"); assert_shown ("gcr-prompter.desktop", FALSE, "KDE"); assert_shown ("gcr-prompter.desktop", FALSE, "GNOME:GNOME-Classic"); assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic:GNOME"); assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic"); assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic:KDE"); assert_shown ("gcr-prompter.desktop", TRUE, "KDE:GNOME-Classic"); assert_shown ("invalid-desktop.desktop", TRUE, "GNOME"); assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop"); assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop:../invalid/desktop"); } static void on_launch_started (GAppLaunchContext *context, GAppInfo *info, GVariant *platform_data, gpointer data) { gboolean *invoked = data; g_assert_true (G_IS_APP_LAUNCH_CONTEXT (context)); g_assert_true (G_IS_APP_INFO (info)); /* Our default context doesn't fill in any platform data */ g_assert_null (platform_data); g_assert_false (*invoked); *invoked = TRUE; } /* Test g_desktop_app_info_launch_uris_as_manager() and * g_desktop_app_info_launch_uris_as_manager_with_fds() */ static void test_launch_as_manager (void) { GDesktopAppInfo *appinfo; GError *error = NULL; gboolean retval; const gchar *path; gboolean invoked = FALSE; GAppLaunchContext *context; if (g_getenv ("DISPLAY") == NULL || g_getenv ("DISPLAY")[0] == '\0') { g_test_skip ("No DISPLAY. Skipping test."); return; } path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL); appinfo = g_desktop_app_info_new_from_filename (path); if (appinfo == NULL) { g_test_skip ("appinfo-test binary not installed"); return; } context = g_app_launch_context_new (); g_signal_connect (context, "launch-started", G_CALLBACK (on_launch_started), &invoked); retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, context, 0, NULL, NULL, NULL, NULL, &error); g_assert_no_error (error); g_assert_true (retval); g_assert_true (invoked); invoked = FALSE; retval = g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo, NULL, context, 0, NULL, NULL, NULL, NULL, -1, -1, -1, &error); g_assert_no_error (error); g_assert_true (retval); g_assert_true (invoked); g_object_unref (appinfo); g_assert_finalize_object (context); } static GAppInfo * create_app_info_toucher (const char *name, const char *touched_file_name, const char *handled_type, char **out_file_path) { GError *error = NULL; GAppInfo *info; gchar *command_line; gchar *file_path; gchar *tmpdir; g_assert_nonnull (out_file_path); tmpdir = g_dir_make_tmp ("desktop-app-info-launch-XXXXXX", &error); g_assert_no_error (error); file_path = g_build_filename (tmpdir, touched_file_name, NULL); command_line = g_strdup_printf ("touch %s", file_path); info = create_command_line_app_info (name, command_line, handled_type); *out_file_path = g_steal_pointer (&file_path); g_free (tmpdir); g_free (command_line); return info; } static void test_default_uri_handler (void) { GError *error = NULL; gchar *file_path = NULL; GAppInfo *info; info = create_app_info_toucher ("Touch Handled", "handled", "x-scheme-handler/glib-touch", &file_path); g_assert_true (G_IS_APP_INFO (info)); g_assert_nonnull (file_path); g_assert_true (g_app_info_launch_default_for_uri ("glib-touch://touch-me", NULL, &error)); g_assert_no_error (error); while (!g_file_test (file_path, G_FILE_TEST_IS_REGULAR)); g_assert_true (g_file_test (file_path, G_FILE_TEST_IS_REGULAR)); g_assert_false (g_app_info_launch_default_for_uri ("glib-INVALID-touch://touch-me", NULL, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_clear_error (&error); g_object_unref (info); g_free (file_path); } static void on_launch_default_for_uri_success_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GError *error = NULL; gboolean *called = user_data; g_assert_true (g_app_info_launch_default_for_uri_finish (result, &error)); g_assert_no_error (error); *called = TRUE; } static void on_launch_default_for_uri_not_found_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GError *error = NULL; GMainLoop *loop = user_data; g_assert_false (g_app_info_launch_default_for_uri_finish (result, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED); g_clear_error (&error); g_main_loop_quit (loop); } static void on_launch_default_for_uri_cancelled_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GError *error = NULL; GMainLoop *loop = user_data; g_assert_false (g_app_info_launch_default_for_uri_finish (result, &error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_main_loop_quit (loop); } static void test_default_uri_handler_async (void) { GCancellable *cancellable; gchar *file_path = NULL; GAppInfo *info; GMainLoop *loop; gboolean called = FALSE; loop = g_main_loop_new (NULL, FALSE); info = create_app_info_toucher ("Touch Handled", "handled-async", "x-scheme-handler/glib-async-touch", &file_path); g_assert_true (G_IS_APP_INFO (info)); g_assert_nonnull (file_path); g_app_info_launch_default_for_uri_async ("glib-async-touch://touch-me", NULL, NULL, on_launch_default_for_uri_success_cb, &called); while (!g_file_test (file_path, G_FILE_TEST_IS_REGULAR) || !called) g_main_context_iteration (NULL, FALSE); g_assert_true (called); g_assert_true (g_file_test (file_path, G_FILE_TEST_IS_REGULAR)); g_unlink (file_path); g_assert_false (g_file_test (file_path, G_FILE_TEST_IS_REGULAR)); g_app_info_launch_default_for_uri_async ("glib-async-INVALID-touch://touch-me", NULL, NULL, on_launch_default_for_uri_not_found_cb, loop); g_main_loop_run (loop); cancellable = g_cancellable_new (); g_app_info_launch_default_for_uri_async ("glib-async-touch://touch-me", NULL, cancellable, on_launch_default_for_uri_cancelled_cb, loop); g_cancellable_cancel (cancellable); g_main_loop_run (loop); /* Once started our touch app may take some time before having written the * file, so let's wait a bit here before ensuring that the file has been * created as expected. */ g_usleep (G_USEC_PER_SEC / 10); g_assert_false (g_file_test (file_path, G_FILE_TEST_IS_REGULAR)); g_object_unref (info); g_main_loop_unref (loop); g_free (file_path); } /* Test if Desktop-File Id is correctly formed */ static void test_id (void) { gchar *result; result = run_apps ("default-for-type", "application/vnd.kde.okular-archive", TRUE, FALSE, NULL, NULL, NULL); g_assert_cmpstr (result, ==, "kde4-okular.desktop\n"); g_free (result); } static const char * get_terminal_divider (const char *terminal_name) { if (g_str_equal (terminal_name, "gnome-terminal")) return "--"; if (g_str_equal (terminal_name, "tilix")) return "-e"; if (g_str_equal (terminal_name, "konsole")) return "-e"; if (g_str_equal (terminal_name, "nxterm")) return "-e"; if (g_str_equal (terminal_name, "color-xterm")) return "-e"; if (g_str_equal (terminal_name, "rxvt")) return "-e"; if (g_str_equal (terminal_name, "dtterm")) return "-e"; if (g_str_equal (terminal_name, "xterm")) return "-e"; if (g_str_equal (terminal_name, "mate-terminal")) return "-x"; if (g_str_equal (terminal_name, "xfce4-terminal")) return "-x"; g_return_val_if_reached (NULL); } static void test_launch_uris_with_terminal (gconstpointer data) { int fds[2]; const char *terminal_exec = data; char *old_path; char *command_line; char *bin_path; char *terminal_path; char *output_fd_path; char *script_contents; char *output_contents; char *sh; GAppInfo *app_info; GList *uris; GList *paths; GStrv output_args; GError *error = NULL; GInputStream *input_stream; GDataInputStream *data_input_stream; sh = g_find_program_in_path ("sh"); g_assert_nonnull (sh); bin_path = g_dir_make_tmp ("bin-path-XXXXXX", &error); g_assert_no_error (error); old_path = g_strdup (g_getenv ("PATH")); g_assert_true (g_setenv ("PATH", bin_path, TRUE)); g_unix_open_pipe (fds, FD_CLOEXEC, &error); g_assert_no_error (error); terminal_path = g_build_filename (bin_path, terminal_exec, NULL); output_fd_path = g_strdup_printf (G_DIR_SEPARATOR_S "proc" G_DIR_SEPARATOR_S "%" G_PID_FORMAT G_DIR_SEPARATOR_S "fd" G_DIR_SEPARATOR_S "%d", getpid (), fds[0]); input_stream = g_unix_input_stream_new (fds[0], TRUE); data_input_stream = g_data_input_stream_new (input_stream); script_contents = g_strdup_printf ("#!%s\n" \ "out='%s'\n" "printf '%%s\\n' \"$*\" > \"$out\"\n", sh, output_fd_path); g_file_set_contents (terminal_path, script_contents, -1, &error); g_assert_no_error (error); g_assert_cmpint (g_chmod (terminal_path, 0500), ==, 0); g_test_message ("Fake '%s' terminal created as: %s", terminal_exec, terminal_path); command_line = g_strdup_printf ("true %s-argument", terminal_exec); app_info = g_app_info_create_from_commandline (command_line, "Test App on Terminal", G_APP_INFO_CREATE_NEEDS_TERMINAL | G_APP_INFO_CREATE_SUPPORTS_URIS, &error); g_assert_no_error (error); paths = g_list_prepend (NULL, bin_path); uris = g_list_prepend (NULL, g_filename_to_uri (bin_path, NULL, &error)); g_assert_no_error (error); paths = g_list_prepend (paths, (gpointer) g_get_user_data_dir ()); uris = g_list_append (uris, g_filename_to_uri (g_get_user_data_dir (), NULL, &error)); g_assert_no_error (error); g_assert_cmpint (g_list_length (paths), ==, 2); g_app_info_launch_uris (app_info, uris, NULL, &error); g_assert_no_error (error); output_contents = g_data_input_stream_read_line (data_input_stream, NULL, NULL, &error); g_assert_no_error (error); g_test_message ("'%s' called with arguments: '%s'", terminal_exec, output_contents); output_args = g_strsplit (output_contents, " ", -1); g_clear_pointer (&output_contents, g_free); g_assert_cmpuint (g_strv_length (output_args), ==, 4); g_assert_cmpstr (output_args[0], ==, get_terminal_divider (terminal_exec)); g_assert_cmpstr (output_args[1], ==, "true"); g_assert_cmpstr (output_args[2], ==, command_line + 5); paths = g_list_delete_link (paths, g_list_find_custom (paths, output_args[3], g_str_equal)); g_assert_cmpint (g_list_length (paths), ==, 1); g_clear_pointer (&output_args, g_strfreev); output_contents = g_data_input_stream_read_line (data_input_stream, NULL, NULL, &error); g_assert_no_error (error); g_test_message ("'%s' called with arguments: '%s'", terminal_exec, output_contents); output_args = g_strsplit (output_contents, " ", -1); g_clear_pointer (&output_contents, g_free); g_assert_cmpuint (g_strv_length (output_args), ==, 4); g_assert_cmpstr (output_args[0], ==, get_terminal_divider (terminal_exec)); g_assert_cmpstr (output_args[1], ==, "true"); g_assert_cmpstr (output_args[2], ==, command_line + 5); paths = g_list_delete_link (paths, g_list_find_custom (paths, output_args[3], g_str_equal)); g_assert_cmpint (g_list_length (paths), ==, 0); g_clear_pointer (&output_args, g_strfreev); g_assert_null (paths); g_assert_true (g_setenv ("PATH", old_path, TRUE)); g_close (fds[1], &error); g_assert_no_error (error); g_free (sh); g_free (command_line); g_free (bin_path); g_free (terminal_path); g_free (output_fd_path); g_free (script_contents); g_free (old_path); g_clear_pointer (&output_args, g_strfreev); g_clear_pointer (&output_contents, g_free); g_clear_object (&data_input_stream); g_clear_object (&input_stream); g_clear_object (&app_info); g_clear_error (&error); g_clear_list (&paths, NULL); g_clear_list (&uris, g_free); } static void test_launch_uris_with_invalid_terminal (void) { char *old_path; char *bin_path; GAppInfo *app_info; GError *error = NULL; bin_path = g_dir_make_tmp ("bin-path-XXXXXX", &error); g_assert_no_error (error); old_path = g_strdup (g_getenv ("PATH")); g_assert_true (g_setenv ("PATH", bin_path, TRUE)); app_info = g_app_info_create_from_commandline ("true invalid-glib-terminal", "Test App on Invalid Terminal", G_APP_INFO_CREATE_NEEDS_TERMINAL | G_APP_INFO_CREATE_SUPPORTS_URIS, &error); g_assert_no_error (error); g_app_info_launch_uris (app_info, NULL, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_clear_error (&error); g_assert_true (g_setenv ("PATH", old_path, TRUE)); g_clear_object (&app_info); g_clear_error (&error); g_free (bin_path); g_free (old_path); } int main (int argc, char *argv[]) { guint i; const gchar *supported_terminals[] = { "gnome-terminal", "mate-terminal", "xfce4-terminal", "tilix", "konsole", "nxterm", "color-xterm", "rxvt", "dtterm", "xterm", }; /* 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, 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); g_test_add_func ("/desktop-app-info/default-async", test_default_async); g_test_add_func ("/desktop-app-info/fallback", test_fallback); g_test_add_func ("/desktop-app-info/lastused", test_last_used); g_test_add_func ("/desktop-app-info/extra-getters", test_extra_getters); g_test_add_func ("/desktop-app-info/actions", test_actions); g_test_add_func ("/desktop-app-info/search", test_search); g_test_add_func ("/desktop-app-info/implements", test_implements); 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); g_test_add_func ("/desktop-app-info/launch-default-uri-handler", test_default_uri_handler); g_test_add_func ("/desktop-app-info/launch-default-uri-handler-async", test_default_uri_handler_async); g_test_add_func ("/desktop-app-info/id", test_id); for (i = 0; i < G_N_ELEMENTS (supported_terminals); i++) { char *path; path = g_strdup_printf ("/desktop-app-info/launch-uris-with-terminal/%s", supported_terminals[i]); g_test_add_data_func (path, supported_terminals[i], test_launch_uris_with_terminal); g_free (path); } g_test_add_func ("/desktop-app-info/launch-uris-with-terminal/invalid-glib-terminal", test_launch_uris_with_invalid_terminal); return g_test_run (); }