glib/gio/tests/desktop-app-info.c
Ray Strode 4657e9bf60 tests/desktop-app-info: Use named pipe instead of /proc
Freebsd doesn't always have /proc mounted, so relying on
/proc for the tests isn't ideal.

This commit changes the desktop-app-info tests to use
mkfifo instead of /proc/../fd/.. to relay terminal
arguments.

Might help with this error message I'm seeing in CI:

/tmp/bin-path-H1UQT1/gnome-terminal: cannot create /proc/38961/fd/6: No such file or directory
2022-10-18 12:15:10 -04:00

1469 lines
47 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include <locale.h>
#include <glib/glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include <gio/gunixinputstream.h>
#include <glib-unix.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
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);
}
#if !defined(__FreeBSD__)
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);
}
#endif
static void
test_launch_uris_with_terminal (gconstpointer data)
{
#if defined(__FreeBSD__)
/* FIXME: https://gitlab.gnome.org/GNOME/glib/-/issues/2781 */
g_test_skip ("/proc pipe sharing currently doesnt work reliably on FreeBSD CI");
#else
int fd;
int ret;
int flags;
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 = NULL;
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));
terminal_path = g_build_filename (bin_path, terminal_exec, NULL);
output_fd_path = g_build_filename (bin_path, "fifo", NULL);
ret = mkfifo (output_fd_path, 0600);
g_assert_cmpint (ret, ==, 0);
fd = g_open (output_fd_path, O_RDONLY | O_CLOEXEC | O_NONBLOCK, 0);
g_assert_cmpint (fd, >=, 0);
flags = fcntl (fd, F_GETFL);
g_assert_cmpint (flags, >=, 0);
ret = fcntl (fd, F_SETFL, flags & ~O_NONBLOCK);
g_assert_cmpint (ret, ==, 0);
input_stream = g_unix_input_stream_new (fd, 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);
while (output_contents == NULL)
{
output_contents =
g_data_input_stream_read_upto (data_input_stream, "\n", 1, NULL, NULL, &error);
g_assert_no_error (error);
if (output_contents == NULL)
g_usleep (G_USEC_PER_SEC / 10);
}
g_test_message ("'%s' called with arguments: '%s'", terminal_exec, output_contents);
g_data_input_stream_read_byte (data_input_stream, NULL, &error);
g_assert_no_error (error);
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);
while (output_contents == NULL)
{
output_contents =
g_data_input_stream_read_upto (data_input_stream, "\n", 1, NULL, NULL, &error);
g_assert_no_error (error);
if (output_contents == NULL)
g_usleep (G_USEC_PER_SEC / 10);
}
g_test_message ("'%s' called with arguments: '%s'", terminal_exec, output_contents);
g_data_input_stream_read_byte (data_input_stream, NULL, &error);
g_assert_no_error (error);
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 (fd, &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);
#endif
}
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 ();
}