1  /* 
       2   * Copyright (C) 2008 Red Hat, Inc
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General
      17   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   * 
      19   * Author: Matthias Clasen
      20   */
      21  
      22  #include <locale.h>
      23  
      24  #include <glib/glib.h>
      25  #include <glib/gstdio.h>
      26  #include <gio/gio.h>
      27  #include <gio/gdesktopappinfo.h>
      28  #include <gio/gunixinputstream.h>
      29  #include <glib-unix.h>
      30  #include <stdlib.h>
      31  #include <string.h>
      32  #include <unistd.h>
      33  #include <sys/types.h>
      34  #include <sys/stat.h>
      35  
      36  G_DECLARE_FINAL_TYPE (TestLaunchContext, test_launch_context, TEST,
      37                        LAUNCH_CONTEXT, GAppLaunchContext);
      38  
      39  struct _TestLaunchContext {
      40    GAppLaunchContext parent;
      41  
      42    char *overriden_startup_notify_id;
      43  };
      44  
      45  struct _TestLaunchContextClass {
      46    GAppLaunchContextClass parent;
      47  };
      48  
      49  G_DEFINE_FINAL_TYPE (TestLaunchContext, test_launch_context,
      50                       G_TYPE_APP_LAUNCH_CONTEXT);
      51  
      52  static void
      53  test_launch_context_init (TestLaunchContext *test_context)
      54  {
      55  }
      56  
      57  static char *
      58  test_launch_context_get_startup_notify_id (GAppLaunchContext *context,
      59                                             GAppInfo *app_info,
      60                                             GList *files)
      61  {
      62    TestLaunchContext *test_context = TEST_LAUNCH_CONTEXT (context);
      63  
      64    if (test_context->overriden_startup_notify_id)
      65      return g_strdup (test_context->overriden_startup_notify_id);
      66  
      67    if (g_app_info_get_id (app_info))
      68      return g_strdup (g_app_info_get_id (app_info));
      69  
      70    if (g_app_info_get_display_name (app_info))
      71      return g_strdup (g_app_info_get_display_name (app_info));
      72  
      73    return g_strdup (g_app_info_get_commandline (app_info));
      74  }
      75  
      76  static void
      77  test_launch_context_get_startup_notify_dispose (GObject *object)
      78  {
      79    TestLaunchContext *test_context = TEST_LAUNCH_CONTEXT (object);
      80  
      81    g_clear_pointer (&test_context->overriden_startup_notify_id, g_free);
      82    G_OBJECT_CLASS (test_launch_context_parent_class)->dispose (object);
      83  }
      84  
      85  static void
      86  test_launch_context_class_init (TestLaunchContextClass *klass)
      87  {
      88    G_APP_LAUNCH_CONTEXT_CLASS (klass)->get_startup_notify_id = test_launch_context_get_startup_notify_id;
      89    G_OBJECT_CLASS (klass)->dispose = test_launch_context_get_startup_notify_dispose;
      90  }
      91  
      92  static GAppInfo *
      93  create_command_line_app_info (const char *name,
      94                                const char *command_line,
      95                                const char *default_for_type)
      96  {
      97    GAppInfo *info;
      98    GError *error = NULL;
      99  
     100    info = g_app_info_create_from_commandline (command_line,
     101                                               name,
     102                                               G_APP_INFO_CREATE_NONE,
     103                                               &error);
     104    g_assert_no_error (error);
     105  
     106    g_app_info_set_as_default_for_type (info, default_for_type, &error);
     107    g_assert_no_error (error);
     108  
     109    return g_steal_pointer (&info);
     110  }
     111  
     112  static GAppInfo *
     113  create_app_info (const char *name)
     114  {
     115    GError *error = NULL;
     116    GAppInfo *info;
     117  
     118    info = create_command_line_app_info (name, "true blah", "application/x-blah");
     119  
     120    /* this is necessary to ensure that the info is saved */
     121    g_app_info_remove_supports_type (info, "application/x-blah", &error);
     122    g_assert_no_error (error);
     123    g_app_info_reset_type_associations ("application/x-blah");
     124    
     125    return info;
     126  }
     127  
     128  static void
     129  test_delete (void)
     130  {
     131    GAppInfo *info;
     132  
     133    const char *id;
     134    char *filename;
     135    gboolean res;
     136  
     137    info = create_app_info ("Blah");
     138   
     139    id = g_app_info_get_id (info);
     140    g_assert_nonnull (id);
     141  
     142    filename = g_build_filename (g_get_user_data_dir (), "applications", id, NULL);
     143  
     144    res = g_file_test (filename, G_FILE_TEST_EXISTS);
     145    g_assert_true (res);
     146  
     147    res = g_app_info_can_delete (info);
     148    g_assert_true (res);
     149  
     150    res = g_app_info_delete (info);
     151    g_assert_true (res);
     152  
     153    res = g_file_test (filename, G_FILE_TEST_EXISTS);
     154    g_assert_false (res);
     155  
     156    g_object_unref (info);
     157  
     158    if (g_file_test ("/usr/share/applications/gedit.desktop", G_FILE_TEST_EXISTS))
     159      {
     160        info = (GAppInfo*)g_desktop_app_info_new_from_filename ("/usr/share/applications/gedit.desktop");
     161        g_assert_nonnull (info);
     162       
     163        res = g_app_info_can_delete (info);
     164        g_assert_false (res);
     165   
     166        res = g_app_info_delete (info);
     167        g_assert_false (res);
     168      }
     169  
     170    g_free (filename);
     171  }
     172  
     173  static void
     174  test_default (void)
     175  {
     176    GAppInfo *info, *info1, *info2, *info3;
     177    GList *list;
     178    GError *error = NULL;  
     179  
     180    info1 = create_app_info ("Blah1");
     181    info2 = create_app_info ("Blah2");
     182    info3 = create_app_info ("Blah3");
     183  
     184    g_app_info_set_as_default_for_type (info1, "application/x-test", &error);
     185    g_assert_no_error (error);
     186  
     187    g_app_info_set_as_default_for_type (info2, "application/x-test", &error);
     188    g_assert_no_error (error);
     189  
     190    info = g_app_info_get_default_for_type ("application/x-test", FALSE);
     191    g_assert_nonnull (info);
     192    g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2));
     193    g_object_unref (info);
     194  
     195    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     196                           "*assertion*uri_scheme*failed*");
     197    g_assert_null (g_app_info_get_default_for_uri_scheme (NULL));
     198    g_test_assert_expected_messages ();
     199  
     200    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     201                           "*assertion*uri_scheme*failed*");
     202    g_assert_null (g_app_info_get_default_for_uri_scheme (""));
     203    g_test_assert_expected_messages ();
     204  
     205    g_app_info_set_as_default_for_type (info3, "x-scheme-handler/glib", &error);
     206    g_assert_no_error (error);
     207    info = g_app_info_get_default_for_uri_scheme ("glib");
     208    g_assert_nonnull (info);
     209    g_assert_true (g_app_info_equal (info, info3));
     210    g_object_unref (info);
     211  
     212    /* now try adding something, but not setting as default */
     213    g_app_info_add_supports_type (info3, "application/x-test", &error);
     214    g_assert_no_error (error);
     215  
     216    /* check that info2 is still default */
     217    info = g_app_info_get_default_for_type ("application/x-test", FALSE);
     218    g_assert_nonnull (info);
     219    g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2));
     220    g_object_unref (info);
     221  
     222    /* now remove info1 again */
     223    g_app_info_remove_supports_type (info1, "application/x-test", &error);
     224    g_assert_no_error (error);
     225  
     226    /* and make sure info2 is still default */
     227    info = g_app_info_get_default_for_type ("application/x-test", FALSE);
     228    g_assert_nonnull (info);
     229    g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2));
     230    g_object_unref (info);
     231  
     232    /* now clean it all up */
     233    g_app_info_reset_type_associations ("application/x-test");
     234    g_app_info_reset_type_associations ("x-scheme-handler/glib");
     235  
     236    list = g_app_info_get_all_for_type ("application/x-test");
     237    g_assert_null (list);
     238  
     239    list = g_app_info_get_all_for_type ("x-scheme-handler/glib");
     240    g_assert_null (list);
     241  
     242    g_app_info_delete (info1);
     243    g_app_info_delete (info2);
     244    g_app_info_delete (info3);
     245  
     246    g_object_unref (info1);
     247    g_object_unref (info2);
     248    g_object_unref (info3);
     249  }
     250  
     251  typedef struct
     252  {
     253    GAppInfo *expected_info;
     254    GMainLoop *loop;
     255  } DefaultForTypeData;
     256  
     257  static void
     258  ensure_default_type_result (GAppInfo           *info,
     259                              DefaultForTypeData *data,
     260                              GError             *error)
     261  {
     262    if (data->expected_info)
     263      {
     264        g_assert_nonnull (info);
     265        g_assert_no_error (error);
     266        g_assert_true (g_app_info_equal (info, data->expected_info));
     267      }
     268    else
     269      {
     270        g_assert_null (info);
     271        g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
     272      }
     273  
     274    g_main_loop_quit (data->loop);
     275    g_clear_object (&info);
     276    g_clear_error (&error);
     277  }
     278  
     279  static void
     280  on_default_for_type_cb (GObject      *object,
     281                          GAsyncResult *result,
     282                          gpointer      user_data)
     283  {
     284    GAppInfo *info;
     285    GError *error = NULL;
     286    DefaultForTypeData *data = user_data;
     287  
     288    g_assert_null (object);
     289  
     290    info = g_app_info_get_default_for_type_finish (result, &error);
     291  
     292    ensure_default_type_result (info, data, error);
     293  }
     294  
     295  static void
     296  on_default_for_uri_cb (GObject      *object,
     297                         GAsyncResult *result,
     298                         gpointer      user_data)
     299  {
     300    GAppInfo *info;
     301    GError *error = NULL;
     302    DefaultForTypeData *data = user_data;
     303  
     304    g_assert_null (object);
     305  
     306    info = g_app_info_get_default_for_uri_scheme_finish (result, &error);
     307  
     308    ensure_default_type_result (info, data, error);
     309  }
     310  
     311  static void
     312  test_default_async (void)
     313  {
     314    DefaultForTypeData data;
     315    GAppInfo *info1, *info2, *info3;
     316    GList *list;
     317    GError *error = NULL;
     318  
     319    data.loop = g_main_loop_new (NULL, TRUE);
     320  
     321    info1 = create_app_info ("Blah1");
     322    info2 = create_app_info ("Blah2");
     323    info3 = create_app_info ("Blah3");
     324  
     325    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     326                           "*assertion*content_type*failed*");
     327    g_app_info_get_default_for_type_async (NULL, FALSE, NULL, NULL, NULL);
     328    g_test_assert_expected_messages ();
     329  
     330    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     331                           "*assertion*content_type*failed*");
     332    g_app_info_get_default_for_type_async ("", FALSE, NULL, NULL, NULL);
     333    g_test_assert_expected_messages ();
     334  
     335    g_app_info_set_as_default_for_type (info1, "application/x-test", &error);
     336    g_assert_no_error (error);
     337  
     338    g_app_info_set_as_default_for_type (info2, "application/x-test", &error);
     339    g_assert_no_error (error);
     340  
     341    data.expected_info = info2;
     342    g_app_info_get_default_for_type_async ("application/x-test", FALSE,
     343                                           NULL, on_default_for_type_cb, &data);
     344    g_main_loop_run (data.loop);
     345  
     346    /* now try adding something, but not setting as default */
     347    g_app_info_add_supports_type (info3, "application/x-test", &error);
     348    g_assert_no_error (error);
     349  
     350    /* check that info2 is still default */
     351    data.expected_info = info2;
     352    g_app_info_get_default_for_type_async ("application/x-test", FALSE,
     353                                           NULL, on_default_for_type_cb, &data);
     354    g_main_loop_run (data.loop);
     355  
     356    /* now remove info1 again */
     357    g_app_info_remove_supports_type (info1, "application/x-test", &error);
     358    g_assert_no_error (error);
     359  
     360    /* and make sure info2 is still default */
     361    data.expected_info = info2;
     362    g_app_info_get_default_for_type_async ("application/x-test", FALSE,
     363                                           NULL, on_default_for_type_cb, &data);
     364    g_main_loop_run (data.loop);
     365  
     366    g_app_info_set_as_default_for_type (info3, "x-scheme-handler/glib-async", &error);
     367    g_assert_no_error (error);
     368  
     369    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     370                           "*assertion*uri_scheme*failed*");
     371    g_assert_null (g_app_info_get_default_for_uri_scheme (NULL));
     372    g_test_assert_expected_messages ();
     373  
     374    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     375                           "*assertion*uri_scheme*failed*");
     376    g_assert_null (g_app_info_get_default_for_uri_scheme (""));
     377    g_test_assert_expected_messages ();
     378  
     379    data.expected_info = info3;
     380    g_app_info_get_default_for_uri_scheme_async ("glib-async", NULL,
     381                                                 on_default_for_uri_cb, &data);
     382    g_main_loop_run (data.loop);
     383  
     384    /* now clean it all up */
     385    g_app_info_reset_type_associations ("application/x-test");
     386  
     387    data.expected_info = NULL;
     388    g_app_info_get_default_for_type_async ("application/x-test", FALSE,
     389                                           NULL, on_default_for_type_cb, &data);
     390    g_main_loop_run (data.loop);
     391  
     392    g_app_info_reset_type_associations ("x-scheme-handler/glib-async");
     393  
     394    data.expected_info = NULL;
     395    g_app_info_get_default_for_uri_scheme_async ("glib-async", NULL,
     396                                                 on_default_for_uri_cb, &data);
     397    g_main_loop_run (data.loop);
     398  
     399    list = g_app_info_get_all_for_type ("application/x-test");
     400    g_assert_null (list);
     401  
     402    g_app_info_delete (info1);
     403    g_app_info_delete (info2);
     404    g_app_info_delete (info3);
     405  
     406    g_object_unref (info1);
     407    g_object_unref (info2);
     408    g_object_unref (info3);
     409  
     410    g_main_loop_unref (data.loop);
     411  }
     412  
     413  static void
     414  test_fallback (void)
     415  {
     416    GAppInfo *info1, *info2, *app = NULL;
     417    GList *apps, *recomm, *fallback, *list, *l, *m;
     418    GError *error = NULL;
     419    gint old_length;
     420  
     421    info1 = create_app_info ("Test1");
     422    info2 = create_app_info ("Test2");
     423  
     424    g_assert_true (g_content_type_is_a ("text/x-python", "text/plain"));
     425  
     426    apps = g_app_info_get_all_for_type ("text/x-python");
     427    old_length = g_list_length (apps);
     428    g_list_free_full (apps, g_object_unref);
     429  
     430    g_app_info_add_supports_type (info1, "text/x-python", &error);
     431    g_assert_no_error (error);
     432  
     433    g_app_info_add_supports_type (info2, "text/plain", &error);
     434    g_assert_no_error (error);
     435  
     436    /* check that both apps are registered */
     437    apps = g_app_info_get_all_for_type ("text/x-python");
     438    g_assert_cmpint (g_list_length (apps), ==, old_length + 2);
     439  
     440    /* check that Test1 is among the recommended apps */
     441    recomm = g_app_info_get_recommended_for_type ("text/x-python");
     442    g_assert_nonnull (recomm);
     443    for (l = recomm; l; l = l->next)
     444      {
     445        app = l->data;
     446        if (g_app_info_equal (info1, app))
     447          break;
     448      }
     449    g_assert_nonnull (app);
     450    g_assert_true (g_app_info_equal (info1, app));
     451  
     452    /* and that Test2 is among the fallback apps */
     453    fallback = g_app_info_get_fallback_for_type ("text/x-python");
     454    g_assert_nonnull (fallback);
     455    for (l = fallback; l; l = l->next)
     456      {
     457        app = l->data;
     458        if (g_app_info_equal (info2, app))
     459          break;
     460      }
     461    g_assert_cmpstr (g_app_info_get_name (app), ==, "Test2");
     462  
     463    /* check that recomm + fallback = all applications */
     464    list = g_list_concat (g_list_copy (recomm), g_list_copy (fallback));
     465    g_assert_cmpuint (g_list_length (list), ==, g_list_length (apps));
     466  
     467    for (l = list, m = apps; l != NULL && m != NULL; l = l->next, m = m->next)
     468      {
     469        g_assert_true (g_app_info_equal (l->data, m->data));
     470      }
     471  
     472    g_list_free (list);
     473  
     474    g_list_free_full (apps, g_object_unref);
     475    g_list_free_full (recomm, g_object_unref);
     476    g_list_free_full (fallback, g_object_unref);
     477  
     478    g_app_info_reset_type_associations ("text/x-python");
     479    g_app_info_reset_type_associations ("text/plain");
     480  
     481    g_app_info_delete (info1);
     482    g_app_info_delete (info2);
     483  
     484    g_object_unref (info1);
     485    g_object_unref (info2);
     486  }
     487  
     488  static void
     489  test_last_used (void)
     490  {
     491    GList *applications;
     492    GAppInfo *info1, *info2, *default_app;
     493    GError *error = NULL;
     494  
     495    info1 = create_app_info ("Test1");
     496    info2 = create_app_info ("Test2");
     497  
     498    g_app_info_set_as_default_for_type (info1, "application/x-test", &error);
     499    g_assert_no_error (error);
     500  
     501    g_app_info_add_supports_type (info2, "application/x-test", &error);
     502    g_assert_no_error (error);
     503  
     504    applications = g_app_info_get_recommended_for_type ("application/x-test");
     505    g_assert_cmpuint (g_list_length (applications), ==, 2);
     506  
     507    /* the first should be the default app now */
     508    g_assert_true (g_app_info_equal (g_list_nth_data (applications, 0), info1));
     509    g_assert_true (g_app_info_equal (g_list_nth_data (applications, 1), info2));
     510  
     511    g_list_free_full (applications, g_object_unref);
     512  
     513    g_app_info_set_as_last_used_for_type (info2, "application/x-test", &error);
     514    g_assert_no_error (error);
     515  
     516    applications = g_app_info_get_recommended_for_type ("application/x-test");
     517    g_assert_cmpuint (g_list_length (applications), ==, 2);
     518  
     519    default_app = g_app_info_get_default_for_type ("application/x-test", FALSE);
     520    g_assert_true (g_app_info_equal (default_app, info1));
     521  
     522    /* the first should be the other app now */
     523    g_assert_true (g_app_info_equal (g_list_nth_data (applications, 0), info2));
     524    g_assert_true (g_app_info_equal (g_list_nth_data (applications, 1), info1));
     525  
     526    g_list_free_full (applications, g_object_unref);
     527  
     528    g_app_info_reset_type_associations ("application/x-test");
     529  
     530    g_app_info_delete (info1);
     531    g_app_info_delete (info2);
     532  
     533    g_object_unref (info1);
     534    g_object_unref (info2);
     535    g_object_unref (default_app);
     536  }
     537  
     538  static void
     539  test_extra_getters (void)
     540  {
     541    GDesktopAppInfo *appinfo;
     542    const gchar *lang;
     543    gchar *s;
     544    gboolean b;
     545  
     546    lang = setlocale (LC_ALL, NULL);
     547    g_setenv ("LANGUAGE", "de_DE.UTF8", TRUE);
     548    setlocale (LC_ALL, "");
     549  
     550    appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "appinfo-test-static.desktop", NULL));
     551    g_assert_nonnull (appinfo);
     552  
     553    g_assert_true (g_desktop_app_info_has_key (appinfo, "Terminal"));
     554    g_assert_false (g_desktop_app_info_has_key (appinfo, "Bratwurst"));
     555  
     556    s = g_desktop_app_info_get_string (appinfo, "StartupWMClass");
     557    g_assert_cmpstr (s, ==, "appinfo-class");
     558    g_free (s);
     559  
     560    s = g_desktop_app_info_get_locale_string (appinfo, "X-JunkFood");
     561    g_assert_cmpstr (s, ==, "Bratwurst");
     562    g_free (s);
     563  
     564    g_setenv ("LANGUAGE", "sv_SE.UTF8", TRUE);
     565    setlocale (LC_ALL, "");
     566  
     567    s = g_desktop_app_info_get_locale_string (appinfo, "X-JunkFood");
     568    g_assert_cmpstr (s, ==, "Burger"); /* fallback */
     569    g_free (s);
     570  
     571    b = g_desktop_app_info_get_boolean (appinfo, "Terminal");
     572    g_assert_true (b);
     573  
     574    g_object_unref (appinfo);
     575  
     576    g_setenv ("LANGUAGE", lang, TRUE);
     577    setlocale (LC_ALL, "");
     578  }
     579  
     580  static void
     581  wait_for_file (const gchar *want_this,
     582                 const gchar *but_not_this,
     583                 const gchar *or_this)
     584  {
     585    while (access (want_this, F_OK) != 0)
     586      g_usleep (100000); /* 100ms */
     587  
     588    g_assert_cmpuint (access (but_not_this, F_OK), !=, 0);
     589    g_assert_cmpuint (access (or_this, F_OK), !=, 0);
     590  
     591    unlink (want_this);
     592    unlink (but_not_this);
     593    unlink (or_this);
     594  }
     595  
     596  static void
     597  test_actions (void)
     598  {
     599    const char *expected[] = { "frob", "tweak", "twiddle", "broken", NULL };
     600    const gchar * const *actions;
     601    GDesktopAppInfo *appinfo;
     602    const gchar *tmpdir;
     603    gchar *name;
     604    gchar *frob_path;
     605    gchar *tweak_path;
     606    gchar *twiddle_path;
     607  
     608    appinfo = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "appinfo-test-actions.desktop", NULL));
     609    g_assert_nonnull (appinfo);
     610  
     611    actions = g_desktop_app_info_list_actions (appinfo);
     612    g_assert_cmpstrv (actions, expected);
     613  
     614    name = g_desktop_app_info_get_action_name (appinfo, "frob");
     615    g_assert_cmpstr (name, ==, "Frobnicate");
     616    g_free (name);
     617  
     618    name = g_desktop_app_info_get_action_name (appinfo, "tweak");
     619    g_assert_cmpstr (name, ==, "Tweak");
     620    g_free (name);
     621  
     622    name = g_desktop_app_info_get_action_name (appinfo, "twiddle");
     623    g_assert_cmpstr (name, ==, "Twiddle");
     624    g_free (name);
     625  
     626    name = g_desktop_app_info_get_action_name (appinfo, "broken");
     627    g_assert_nonnull (name);
     628    g_assert_true (g_utf8_validate (name, -1, NULL));
     629    g_free (name);
     630  
     631    tmpdir = g_getenv ("G_TEST_TMPDIR");
     632    g_assert_nonnull (tmpdir);
     633    frob_path = g_build_filename (tmpdir, "frob", NULL);
     634    tweak_path = g_build_filename (tmpdir, "tweak", NULL);
     635    twiddle_path = g_build_filename (tmpdir, "twiddle", NULL);
     636  
     637    g_assert_false (g_file_test (frob_path, G_FILE_TEST_EXISTS));
     638    g_assert_false (g_file_test (tweak_path, G_FILE_TEST_EXISTS));
     639    g_assert_false (g_file_test (twiddle_path, G_FILE_TEST_EXISTS));
     640  
     641    g_desktop_app_info_launch_action (appinfo, "frob", NULL);
     642    wait_for_file (frob_path, tweak_path, twiddle_path);
     643  
     644    g_desktop_app_info_launch_action (appinfo, "tweak", NULL);
     645    wait_for_file (tweak_path, frob_path, twiddle_path);
     646  
     647    g_desktop_app_info_launch_action (appinfo, "twiddle", NULL);
     648    wait_for_file (twiddle_path, frob_path, tweak_path);
     649  
     650    g_free (frob_path);
     651    g_free (tweak_path);
     652    g_free (twiddle_path);
     653    g_object_unref (appinfo);
     654  }
     655  
     656  static gchar *
     657  run_apps (const gchar *command,
     658            const gchar *arg,
     659            gboolean     with_usr,
     660            gboolean     with_home,
     661            const gchar *locale_name,
     662            const gchar *language,
     663            const gchar *xdg_current_desktop)
     664  {
     665    gboolean success;
     666    gchar **envp;
     667    gchar **argv;
     668    gint status;
     669    gchar *out;
     670    gchar *argv_str = NULL;
     671  
     672    argv = g_new (gchar *, 4);
     673    argv[0] = g_test_build_filename (G_TEST_BUILT, "apps", NULL);
     674    argv[1] = g_strdup (command);
     675    argv[2] = g_strdup (arg);
     676    argv[3] = NULL;
     677  
     678    g_assert_true (g_file_test (argv[0], G_FILE_TEST_IS_EXECUTABLE));
     679    envp = g_get_environ ();
     680  
     681    if (with_usr)
     682      {
     683        gchar *tmp = g_test_build_filename (G_TEST_DIST, "desktop-files", "usr", NULL);
     684        envp = g_environ_setenv (envp, "XDG_DATA_DIRS", tmp, TRUE);
     685        g_free (tmp);
     686      }
     687    else
     688      envp = g_environ_setenv (envp, "XDG_DATA_DIRS", "/does-not-exist", TRUE);
     689  
     690    if (with_home)
     691      {
     692        gchar *tmp = g_test_build_filename (G_TEST_DIST, "desktop-files", "home", NULL);
     693        envp = g_environ_setenv (envp, "XDG_DATA_HOME", tmp, TRUE);
     694        g_free (tmp);
     695      }
     696    else
     697      envp = g_environ_setenv (envp, "XDG_DATA_HOME", "/does-not-exist", TRUE);
     698  
     699    if (locale_name)
     700      envp = g_environ_setenv (envp, "LC_ALL", locale_name, TRUE);
     701    else
     702      envp = g_environ_setenv (envp, "LC_ALL", "C", TRUE);
     703  
     704    if (language)
     705      envp = g_environ_setenv (envp, "LANGUAGE", language, TRUE);
     706    else
     707      envp = g_environ_unsetenv (envp, "LANGUAGE");
     708  
     709    if (xdg_current_desktop)
     710      envp = g_environ_setenv (envp, "XDG_CURRENT_DESKTOP", xdg_current_desktop, TRUE);
     711    else
     712      envp = g_environ_unsetenv (envp, "XDG_CURRENT_DESKTOP");
     713  
     714    envp = g_environ_setenv (envp, "G_MESSAGES_DEBUG", "", TRUE);
     715  
     716    success = g_spawn_sync (NULL, argv, envp, 0, NULL, NULL, &out, NULL, &status, NULL);
     717    g_assert_true (success);
     718    g_assert_cmpuint (status, ==, 0);
     719  
     720    argv_str = g_strjoinv (" ", argv);
     721    g_test_message ("%s: `%s` returned: %s", G_STRFUNC, argv_str, out);
     722    g_free (argv_str);
     723  
     724    g_strfreev (envp);
     725    g_strfreev (argv);
     726  
     727    return out;
     728  }
     729  
     730  static void
     731  assert_strings_equivalent (const gchar *expected,
     732                             const gchar *result)
     733  {
     734    gchar **expected_words;
     735    gchar **result_words;
     736    gint i, j;
     737  
     738    expected_words = g_strsplit (expected, " ", 0);
     739    result_words = g_strsplit_set (result, " \n", 0);
     740  
     741    for (i = 0; expected_words[i]; i++)
     742      {
     743        for (j = 0; result_words[j]; j++)
     744          if (g_str_equal (expected_words[i], result_words[j]))
     745            goto got_it;
     746  
     747        g_test_fail_printf ("Unable to find expected string '%s' in result '%s'", expected_words[i], result);
     748  
     749  got_it:
     750        continue;
     751      }
     752  
     753    g_assert_cmpint (g_strv_length (expected_words), ==, g_strv_length (result_words));
     754    g_strfreev (expected_words);
     755    g_strfreev (result_words);
     756  }
     757  
     758  static void
     759  assert_list (const gchar *expected,
     760               gboolean     with_usr,
     761               gboolean     with_home,
     762               const gchar *locale_name,
     763               const gchar *language)
     764  {
     765    gchar *result;
     766  
     767    result = run_apps ("list", NULL, with_usr, with_home, locale_name, language, NULL);
     768    g_strchomp (result);
     769    assert_strings_equivalent (expected, result);
     770    g_free (result);
     771  }
     772  
     773  static void
     774  assert_info (const gchar *desktop_id,
     775               const gchar *expected,
     776               gboolean     with_usr,
     777               gboolean     with_home,
     778               const gchar *locale_name,
     779               const gchar *language)
     780  {
     781    gchar *result;
     782  
     783    result = run_apps ("show-info", desktop_id, with_usr, with_home, locale_name, language, NULL);
     784    g_assert_cmpstr (result, ==, expected);
     785    g_free (result);
     786  }
     787  
     788  static void
     789  assert_search (const gchar *search_string,
     790                 const gchar *expected,
     791                 gboolean     with_usr,
     792                 gboolean     with_home,
     793                 const gchar *locale_name,
     794                 const gchar *language)
     795  {
     796    gchar **expected_lines;
     797    gchar **result_lines;
     798    gchar *result;
     799    gint i;
     800  
     801    expected_lines = g_strsplit (expected, "\n", -1);
     802    result = run_apps ("search", search_string, with_usr, with_home, locale_name, language, NULL);
     803    result_lines = g_strsplit (result, "\n", -1);
     804    g_assert_cmpint (g_strv_length (expected_lines), ==, g_strv_length (result_lines));
     805    for (i = 0; expected_lines[i]; i++)
     806      assert_strings_equivalent (expected_lines[i], result_lines[i]);
     807    g_strfreev (expected_lines);
     808    g_strfreev (result_lines);
     809    g_free (result);
     810  }
     811  
     812  static void
     813  assert_implementations (const gchar *interface,
     814                          const gchar *expected,
     815                          gboolean     with_usr,
     816                          gboolean     with_home)
     817  {
     818    gchar *result;
     819  
     820    result = run_apps ("implementations", interface, with_usr, with_home, NULL, NULL, NULL);
     821    g_strchomp (result);
     822    assert_strings_equivalent (expected, result);
     823    g_free (result);
     824  }
     825  
     826  #define ALL_USR_APPS  "evince-previewer.desktop nautilus-classic.desktop gnome-font-viewer.desktop "         \
     827                        "baobab.desktop yelp.desktop eog.desktop cheese.desktop org.gnome.clocks.desktop "         \
     828                        "gnome-contacts.desktop kde4-kate.desktop gcr-prompter.desktop totem.desktop "         \
     829                        "gnome-terminal.desktop nautilus-autorun-software.desktop gcr-viewer.desktop "         \
     830                        "nautilus-connect-server.desktop kde4-dolphin.desktop gnome-music.desktop "            \
     831                        "kde4-konqbrowser.desktop gucharmap.desktop kde4-okular.desktop nautilus.desktop "     \
     832                        "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop " \
     833                        "invalid-desktop.desktop"
     834  #define HOME_APPS     "epiphany-weather-for-toronto-island-9c6a4e022b17686306243dada811d550d25eb1fb.desktop"
     835  #define ALL_HOME_APPS HOME_APPS " eog.desktop"
     836  
     837  static void
     838  test_search (void)
     839  {
     840    assert_list ("", FALSE, FALSE, NULL, NULL);
     841    assert_list (ALL_USR_APPS, TRUE, FALSE, NULL, NULL);
     842    assert_list (ALL_HOME_APPS, FALSE, TRUE, NULL, NULL);
     843    assert_list (ALL_USR_APPS " " HOME_APPS, TRUE, TRUE, NULL, NULL);
     844  
     845    /* The user has "installed" their own version of eog.desktop which
     846     * calls it "Eye of GNOME".  Do some testing based on that.
     847     *
     848     * We should always find "Pictures" keyword no matter where we look.
     849     */
     850    assert_search ("Picture", "eog.desktop\n", TRUE, TRUE, NULL, NULL);
     851    assert_search ("Picture", "eog.desktop\n", TRUE, FALSE, NULL, NULL);
     852    assert_search ("Picture", "eog.desktop\n", FALSE, TRUE, NULL, NULL);
     853    assert_search ("Picture", "", FALSE, FALSE, NULL, NULL);
     854  
     855    /* We should only find it called "eye of gnome" when using the user's
     856     * directory.
     857     */
     858    assert_search ("eye gnome", "", TRUE, FALSE, NULL, NULL);
     859    assert_search ("eye gnome", "eog.desktop\n", FALSE, TRUE, NULL, NULL);
     860    assert_search ("eye gnome", "eog.desktop\n", TRUE, TRUE, NULL, NULL);
     861  
     862    /* We should only find it called "image viewer" when _not_ using the
     863     * user's directory.
     864     */
     865    assert_search ("image viewer", "eog.desktop\n", TRUE, FALSE, NULL, NULL);
     866    assert_search ("image viewer", "", FALSE, TRUE, NULL, NULL);
     867    assert_search ("image viewer", "", TRUE, TRUE, NULL, NULL);
     868  
     869    /* There're "flatpak" apps (clocks) installed as well - they should *not*
     870     * match the prefix command ("/bin/sh") in the Exec= line though. Then with
     871     * substring matching, Image Viewer (eog) should be in next group because it
     872     * contains "Slideshow" in its keywords.
     873     */
     874    assert_search ("sh", "gnome-terminal.desktop\n"
     875                         "eog.desktop\n", TRUE, FALSE, NULL, NULL);
     876  
     877    /* "frobnicator.desktop" is ignored by get_all() because the binary is
     878     * missing, but search should still find it (to avoid either stale results
     879     * from the cache or expensive stat() calls for each potential result)
     880     */
     881    assert_search ("frobni", "frobnicator.desktop\n", TRUE, FALSE, NULL, NULL);
     882  
     883    /* Obvious multi-word search */
     884    assert_search ("doc hel", "yelp.desktop\n", TRUE, TRUE, NULL, NULL);
     885  
     886    /* Repeated search terms should do nothing... */
     887    assert_search ("files file fil fi f", "nautilus.desktop\n", TRUE, TRUE, NULL, NULL);
     888  
     889    /* "con" will match "connect" and "contacts" on name with prefix match in
     890     * first group, then second group is a Keyword prefix match for "configuration" in dconf-editor.desktop
     891     * and third group is a substring match for "Desktop Icons" in Name of nautilus-classic.desktop.
     892     */
     893    assert_search ("con", "gnome-contacts.desktop nautilus-connect-server.desktop\n"
     894                          "dconf-editor.desktop\n"
     895                          "nautilus-classic.desktop\n", TRUE, TRUE, NULL, NULL);
     896  
     897    /* "gnome" will match "eye of gnome" from the user's directory, plus
     898     * matching "GNOME Clocks" X-GNOME-FullName.
     899     */
     900    assert_search ("gnome", "eog.desktop\n"
     901                            "org.gnome.clocks.desktop\n", TRUE, TRUE, NULL, NULL);
     902  
     903    /* eog has exec name 'false' in usr only */
     904    assert_search ("false", "eog.desktop\n", TRUE, FALSE, NULL, NULL);
     905    assert_search ("false", "", FALSE, TRUE, NULL, NULL);
     906    assert_search ("false", "", TRUE, TRUE, NULL, NULL);
     907    assert_search ("false", "", FALSE, FALSE, NULL, NULL);
     908  
     909    /* make sure we only search the first component */
     910    assert_search ("nonsearchable", "", TRUE, FALSE, NULL, NULL);
     911  
     912    /* "gnome con" will match only gnome contacts; via the name for
     913     * "contacts" and keywords for "friend"
     914     */
     915    assert_search ("friend con", "gnome-contacts.desktop\n", TRUE, TRUE, NULL, NULL);
     916  
     917    /* make sure we get the correct kde4- prefix on the application IDs
     918     * from subdirectories
     919     */
     920    assert_search ("konq", "kde4-konqbrowser.desktop\n", TRUE, TRUE, NULL, NULL);
     921    assert_search ("kate", "kde4-kate.desktop\n", TRUE, TRUE, NULL, NULL);
     922  
     923    /* make sure we can look up apps by name properly */
     924    assert_info ("kde4-kate.desktop",
     925                 "kde4-kate.desktop\n"
     926                 "Kate\n"
     927                 "Kate\n"
     928                 "nil\n", TRUE, TRUE, NULL, NULL);
     929  
     930    assert_info ("nautilus.desktop",
     931                 "nautilus.desktop\n"
     932                 "Files\n"
     933                 "Files\n"
     934                 "Access and organize files\n", TRUE, TRUE, NULL, NULL);
     935  
     936    /* make sure localised searching works properly */
     937    assert_search ("foliumi", "nautilus.desktop\n"
     938                              "kde4-konqbrowser.desktop\n", TRUE, FALSE, "en_US.UTF-8", "eo");
     939    /* the user's eog.desktop has no translations... */
     940    assert_search ("foliumi", "nautilus.desktop\n"
     941                              "kde4-konqbrowser.desktop\n", TRUE, TRUE, "en_US.UTF-8", "eo");
     942  }
     943  
     944  static void
     945  test_implements (void)
     946  {
     947    /* Make sure we can find our search providers... */
     948    assert_implementations ("org.gnome.Shell.SearchProvider2",
     949                         "gnome-music.desktop gnome-contacts.desktop eog.desktop",
     950                         TRUE, FALSE);
     951  
     952    /* And our image acquisition possibilities... */
     953    assert_implementations ("org.freedesktop.ImageProvider",
     954                         "cheese.desktop",
     955                         TRUE, FALSE);
     956  
     957    /* Make sure the user's eog is properly masking the system one */
     958    assert_implementations ("org.gnome.Shell.SearchProvider2",
     959                         "gnome-music.desktop gnome-contacts.desktop",
     960                         TRUE, TRUE);
     961  
     962    /* Make sure we get nothing if we have nothing */
     963    assert_implementations ("org.gnome.Shell.SearchProvider2", "", FALSE, FALSE);
     964  }
     965  
     966  static void
     967  assert_shown (const gchar *desktop_id,
     968                gboolean     expected,
     969                const gchar *xdg_current_desktop)
     970  {
     971    gchar *result;
     972  
     973    result = run_apps ("should-show", desktop_id, TRUE, TRUE, NULL, NULL, xdg_current_desktop);
     974    g_assert_cmpstr (result, ==, expected ? "true\n" : "false\n");
     975    g_free (result);
     976  }
     977  
     978  static void
     979  test_show_in (void)
     980  {
     981    assert_shown ("gcr-prompter.desktop", FALSE, NULL);
     982    assert_shown ("gcr-prompter.desktop", FALSE, "GNOME");
     983    assert_shown ("gcr-prompter.desktop", FALSE, "KDE");
     984    assert_shown ("gcr-prompter.desktop", FALSE, "GNOME:GNOME-Classic");
     985    assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic:GNOME");
     986    assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic");
     987    assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic:KDE");
     988    assert_shown ("gcr-prompter.desktop", TRUE, "KDE:GNOME-Classic");
     989    assert_shown ("invalid-desktop.desktop", TRUE, "GNOME");
     990    assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop");
     991    assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop:../invalid/desktop");
     992  }
     993  
     994  static void
     995  on_launch_started (GAppLaunchContext *context, GAppInfo *info, GVariant *platform_data, gpointer data)
     996  {
     997    gboolean *invoked = data;
     998  
     999    g_assert_true (G_IS_APP_LAUNCH_CONTEXT (context));
    1000  
    1001    if (TEST_IS_LAUNCH_CONTEXT (context))
    1002      {
    1003        GVariantDict dict;
    1004        const char *sni;
    1005        char *expected_sni;
    1006  
    1007        g_assert_nonnull (platform_data);
    1008        g_variant_dict_init (&dict, platform_data);
    1009        g_assert_true (
    1010          g_variant_dict_lookup (&dict, "startup-notification-id", "&s", &sni));
    1011        expected_sni = g_app_launch_context_get_startup_notify_id (context, info, NULL);
    1012        g_assert_cmpstr (sni, ==, expected_sni);
    1013  
    1014        g_free (expected_sni);
    1015        g_variant_dict_clear (&dict);
    1016      }
    1017    else
    1018      {
    1019        /* Our default context doesn't fill in any platform data */
    1020        g_assert_null (platform_data);
    1021      }
    1022  
    1023    g_assert_false (*invoked);
    1024    *invoked = TRUE;
    1025  }
    1026  
    1027  static void
    1028  on_launched (GAppLaunchContext *context, GAppInfo *info, GVariant *platform_data, gpointer data)
    1029  {
    1030    gboolean *launched = data;
    1031    GVariantDict dict;
    1032    int pid;
    1033  
    1034    g_assert_true (G_IS_APP_LAUNCH_CONTEXT (context));
    1035    g_assert_true (G_IS_APP_INFO (info));
    1036    g_assert_nonnull (platform_data);
    1037    g_variant_dict_init (&dict, platform_data);
    1038    g_assert_true (g_variant_dict_lookup (&dict, "pid", "i", &pid, NULL));
    1039    g_assert_cmpint (pid, >, 1);
    1040  
    1041    g_assert_false (*launched);
    1042    *launched = TRUE;
    1043  
    1044    g_variant_dict_clear (&dict);
    1045  }
    1046  
    1047  static void
    1048  on_launch_failed (GAppLaunchContext *context, const char *startup_notify_id, gpointer data)
    1049  {
    1050    gboolean *invoked = data;
    1051  
    1052    g_assert_true (G_IS_APP_LAUNCH_CONTEXT (context));
    1053    g_assert_nonnull (startup_notify_id);
    1054    g_test_message ("Application launch failed: %s", startup_notify_id);
    1055  
    1056    g_assert_false (*invoked);
    1057    *invoked = TRUE;
    1058  }
    1059  
    1060  /* Test g_desktop_app_info_launch_uris_as_manager() and
    1061   * g_desktop_app_info_launch_uris_as_manager_with_fds()
    1062   */
    1063  static void
    1064  test_launch_as_manager (void)
    1065  {
    1066    GDesktopAppInfo *appinfo;
    1067    GError *error = NULL;
    1068    gboolean retval;
    1069    const gchar *path;
    1070    gboolean invoked = FALSE;
    1071    gboolean launched = FALSE;
    1072    gboolean failed = FALSE;
    1073    GAppLaunchContext *context;
    1074  
    1075    path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL);
    1076    appinfo = g_desktop_app_info_new_from_filename (path);
    1077    g_assert_true (G_IS_APP_INFO (appinfo));
    1078  
    1079    context = g_object_new (test_launch_context_get_type (), NULL);
    1080    g_signal_connect (context, "launch-started",
    1081                      G_CALLBACK (on_launch_started),
    1082                      &invoked);
    1083    g_signal_connect (context, "launched",
    1084                      G_CALLBACK (on_launched),
    1085                      &launched);
    1086    g_signal_connect (context, "launch-failed",
    1087                      G_CALLBACK (on_launch_failed),
    1088                      &failed);
    1089    retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, context, 0,
    1090                                                        NULL, NULL,
    1091                                                        NULL, NULL,
    1092                                                        &error);
    1093    g_assert_no_error (error);
    1094    g_assert_true (retval);
    1095    g_assert_true (invoked);
    1096    g_assert_true (launched);
    1097    g_assert_false (failed);
    1098  
    1099    invoked = FALSE;
    1100    launched = FALSE;
    1101    failed = FALSE;
    1102    retval = g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
    1103                                                                 NULL, context, 0,
    1104                                                                 NULL, NULL,
    1105                                                                 NULL, NULL,
    1106                                                                 -1, -1, -1,
    1107                                                                 &error);
    1108    g_assert_no_error (error);
    1109    g_assert_true (retval);
    1110    g_assert_true (invoked);
    1111    g_assert_true (launched);
    1112    g_assert_false (failed);
    1113  
    1114    g_object_unref (appinfo);
    1115    g_assert_finalize_object (context);
    1116  }
    1117  
    1118  static void
    1119  test_launch_as_manager_fail (void)
    1120  {
    1121    GAppLaunchContext *context;
    1122    GDesktopAppInfo *appinfo;
    1123    GError *error = NULL;
    1124    gboolean retval;
    1125    const gchar *path;
    1126    gboolean launch_started = FALSE;
    1127    gboolean launched = FALSE;
    1128    gboolean failed = FALSE;
    1129  
    1130    g_test_summary ("Tests that launch-errors are properly handled, we force " \
    1131                    "this by using invalid FD's values when launching as manager");
    1132  
    1133    path = g_test_get_filename (G_TEST_BUILT, "appinfo-test.desktop", NULL);
    1134    appinfo = g_desktop_app_info_new_from_filename (path);
    1135    g_assert_true (G_IS_APP_INFO (appinfo));
    1136  
    1137    context = g_object_new (test_launch_context_get_type (), NULL);
    1138    g_signal_connect (context, "launch-started",
    1139                      G_CALLBACK (on_launch_started),
    1140                      &launch_started);
    1141    g_signal_connect (context, "launched",
    1142                      G_CALLBACK (on_launched),
    1143                      &launched);
    1144    g_signal_connect (context, "launch-failed",
    1145                      G_CALLBACK (on_launch_failed),
    1146                      &failed);
    1147  
    1148    retval = g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo,
    1149                                                                 NULL, context, 0,
    1150                                                                 NULL, NULL,
    1151                                                                 NULL, NULL,
    1152                                                                 3000, 3001, 3002,
    1153                                                                 &error);
    1154    g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED);
    1155    g_assert_false (retval);
    1156    g_assert_true (launch_started);
    1157    g_assert_false (launched);
    1158    g_assert_true (failed);
    1159  
    1160    g_clear_error (&error);
    1161    g_object_unref (appinfo);
    1162    g_assert_finalize_object (context);
    1163  }
    1164  
    1165  static GAppInfo *
    1166  create_app_info_toucher (const char  *name,
    1167                           const char  *touched_file_name,
    1168                           const char  *handled_type,
    1169                           char       **out_file_path)
    1170  {
    1171    GError *error = NULL;
    1172    GAppInfo *info;
    1173    gchar *command_line;
    1174    gchar *file_path;
    1175    gchar *tmpdir;
    1176  
    1177    g_assert_nonnull (out_file_path);
    1178  
    1179    tmpdir = g_dir_make_tmp ("desktop-app-info-launch-XXXXXX", &error);
    1180    g_assert_no_error (error);
    1181  
    1182    file_path = g_build_filename (tmpdir, touched_file_name, NULL);
    1183    command_line = g_strdup_printf ("touch %s", file_path);
    1184  
    1185    info = create_command_line_app_info (name, command_line, handled_type);
    1186    *out_file_path = g_steal_pointer (&file_path);
    1187  
    1188    g_free (tmpdir);
    1189    g_free (command_line);
    1190  
    1191    return info;
    1192  }
    1193  
    1194  static void
    1195  test_default_uri_handler (void)
    1196  {
    1197    GError *error = NULL;
    1198    gchar *file_path = NULL;
    1199    GAppInfo *info;
    1200  
    1201    info = create_app_info_toucher ("Touch Handled", "handled",
    1202                                    "x-scheme-handler/glib-touch",
    1203                                    &file_path);
    1204    g_assert_true (G_IS_APP_INFO (info));
    1205    g_assert_nonnull (file_path);
    1206  
    1207    g_assert_true (g_app_info_launch_default_for_uri ("glib-touch://touch-me",
    1208                                                      NULL, &error));
    1209    g_assert_no_error (error);
    1210  
    1211    while (!g_file_test (file_path, G_FILE_TEST_IS_REGULAR));
    1212    g_assert_true (g_file_test (file_path, G_FILE_TEST_IS_REGULAR));
    1213  
    1214    g_assert_false (g_app_info_launch_default_for_uri ("glib-INVALID-touch://touch-me",
    1215                                                       NULL, &error));
    1216    g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
    1217    g_clear_error (&error);
    1218  
    1219    g_object_unref (info);
    1220    g_free (file_path);
    1221  }
    1222  
    1223  static void
    1224  on_launch_default_for_uri_success_cb (GObject      *object,
    1225                                        GAsyncResult *result,
    1226                                        gpointer      user_data)
    1227  {
    1228    GError *error = NULL;
    1229    gboolean *called = user_data;
    1230  
    1231    g_assert_true (g_app_info_launch_default_for_uri_finish (result, &error));
    1232    g_assert_no_error (error);
    1233  
    1234    *called = TRUE;
    1235  }
    1236  
    1237  static void
    1238  on_launch_default_for_uri_not_found_cb (GObject      *object,
    1239                                          GAsyncResult *result,
    1240                                          gpointer      user_data)
    1241  {
    1242    GError *error = NULL;
    1243    GMainLoop *loop = user_data;
    1244  
    1245    g_assert_false (g_app_info_launch_default_for_uri_finish (result, &error));
    1246    g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
    1247    g_clear_error (&error);
    1248  
    1249    g_main_loop_quit (loop);
    1250  }
    1251  
    1252  static void
    1253  on_launch_default_for_uri_cancelled_cb (GObject      *object,
    1254                                          GAsyncResult *result,
    1255                                          gpointer      user_data)
    1256  {
    1257    GError *error = NULL;
    1258    GMainLoop *loop = user_data;
    1259  
    1260    g_assert_false (g_app_info_launch_default_for_uri_finish (result, &error));
    1261    g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
    1262    g_clear_error (&error);
    1263  
    1264    g_main_loop_quit (loop);
    1265  }
    1266  
    1267  static void
    1268  test_default_uri_handler_async (void)
    1269  {
    1270    GCancellable *cancellable;
    1271    gchar *file_path = NULL;
    1272    GAppInfo *info;
    1273    GMainLoop *loop;
    1274    gboolean called = FALSE;
    1275    gint64 start_time, touch_time;
    1276  
    1277    loop = g_main_loop_new (NULL, FALSE);
    1278    info = create_app_info_toucher ("Touch Handled", "handled-async",
    1279                                    "x-scheme-handler/glib-async-touch",
    1280                                    &file_path);
    1281    g_assert_true (G_IS_APP_INFO (info));
    1282    g_assert_nonnull (file_path);
    1283  
    1284    start_time = g_get_real_time ();
    1285    g_app_info_launch_default_for_uri_async ("glib-async-touch://touch-me", NULL,
    1286                                             NULL,
    1287                                             on_launch_default_for_uri_success_cb,
    1288                                             &called);
    1289  
    1290    while (!g_file_test (file_path, G_FILE_TEST_IS_REGULAR) || !called)
    1291      g_main_context_iteration (NULL, FALSE);
    1292  
    1293    touch_time = g_get_real_time () - start_time;
    1294    g_assert_true (called);
    1295    g_assert_true (g_file_test (file_path, G_FILE_TEST_IS_REGULAR));
    1296  
    1297    g_unlink (file_path);
    1298    g_assert_false (g_file_test (file_path, G_FILE_TEST_IS_REGULAR));
    1299  
    1300    g_app_info_launch_default_for_uri_async ("glib-async-INVALID-touch://touch-me",
    1301                                             NULL, NULL,
    1302                                             on_launch_default_for_uri_not_found_cb,
    1303                                             loop);
    1304    g_main_loop_run (loop);
    1305  
    1306    cancellable = g_cancellable_new ();
    1307    g_app_info_launch_default_for_uri_async ("glib-async-touch://touch-me", NULL,
    1308                                             cancellable,
    1309                                             on_launch_default_for_uri_cancelled_cb,
    1310                                             loop);
    1311    g_cancellable_cancel (cancellable);
    1312    g_main_loop_run (loop);
    1313  
    1314    /* If started, our touch app would take some time to actually write the
    1315     * file to disk, so let's wait a bit here to ensure that the file isn't
    1316     * inadvertently getting created when a launch operation is canceled up
    1317     * front. Give it 3× as long as the successful case took, to allow for 
    1318     * some variance.
    1319     */
    1320    g_usleep (touch_time * 3);
    1321    g_assert_false (g_file_test (file_path, G_FILE_TEST_IS_REGULAR));
    1322  
    1323    g_object_unref (info);
    1324    g_main_loop_unref (loop);
    1325    g_free (file_path);
    1326  }
    1327  
    1328  /* Test if Desktop-File Id is correctly formed */
    1329  static void
    1330  test_id (void)
    1331  {
    1332    gchar *result;
    1333  
    1334    result = run_apps ("default-for-type", "application/vnd.kde.okular-archive",
    1335                       TRUE, FALSE, NULL, NULL, NULL);
    1336    g_assert_cmpstr (result, ==, "kde4-okular.desktop\n");
    1337    g_free (result);
    1338  }
    1339  
    1340  static const char *
    1341  get_terminal_divider (const char *terminal_name)
    1342  {
    1343    if (g_str_equal (terminal_name, "xdg-terminal-exec"))
    1344      return NULL;
    1345    if (g_str_equal (terminal_name, "kgx"))
    1346      return "-e";
    1347    if (g_str_equal (terminal_name, "gnome-terminal"))
    1348      return "--";
    1349    if (g_str_equal (terminal_name, "tilix"))
    1350      return "-e";
    1351    if (g_str_equal (terminal_name, "konsole"))
    1352      return "-e";
    1353    if (g_str_equal (terminal_name, "nxterm"))
    1354      return "-e";
    1355    if (g_str_equal (terminal_name, "color-xterm"))
    1356      return "-e";
    1357    if (g_str_equal (terminal_name, "rxvt"))
    1358      return "-e";
    1359    if (g_str_equal (terminal_name, "dtterm"))
    1360      return "-e";
    1361    if (g_str_equal (terminal_name, "xterm"))
    1362      return "-e";
    1363    if (g_str_equal (terminal_name, "mate-terminal"))
    1364      return "-x";
    1365    if (g_str_equal (terminal_name, "xfce4-terminal"))
    1366      return "-x";
    1367  
    1368    g_return_val_if_reached (NULL);
    1369  }
    1370  
    1371  typedef enum {
    1372    TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_PATH_OVERRIDE,
    1373    TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_CONTEXT,
    1374    TERMINAL_LAUNCH_TYPE_KEY_FILE_WITH_PATH,
    1375  } TerminalLaunchType;
    1376  
    1377  typedef struct {
    1378    const char *exec;
    1379    TerminalLaunchType type;
    1380  } TerminalLaunchData;
    1381  
    1382  static TerminalLaunchData *
    1383  terminal_launch_data_new (const char *exec, TerminalLaunchType type)
    1384  {
    1385    TerminalLaunchData *d = NULL;
    1386  
    1387    d = g_new0 (TerminalLaunchData, 1);
    1388    d->exec = exec;
    1389    d->type = type;
    1390  
    1391    return d;
    1392  }
    1393  
    1394  static void
    1395  test_launch_uris_with_terminal (gconstpointer data)
    1396  {
    1397    int fd;
    1398    int ret;
    1399    int flags;
    1400    int terminal_divider_arg_length;
    1401    const TerminalLaunchData *launch_data = data;
    1402    const char *terminal_exec = launch_data->exec;
    1403    char *old_path = NULL;
    1404    char *command_line;
    1405    char *bin_path;
    1406    char *terminal_path;
    1407    char *output_fd_path;
    1408    char *script_contents;
    1409    char *output_contents = NULL;
    1410    char *sh;
    1411    GAppInfo *app_info;
    1412    GList *uris;
    1413    GList *paths;
    1414    GStrv output_args;
    1415    GError *error = NULL;
    1416    GInputStream *input_stream;
    1417    GDataInputStream *data_input_stream;
    1418    GAppLaunchContext *launch_context;
    1419  
    1420    sh = g_find_program_in_path ("sh");
    1421    g_assert_nonnull (sh);
    1422  
    1423    bin_path = g_dir_make_tmp ("bin-path-XXXXXX", &error);
    1424    g_assert_no_error (error);
    1425  
    1426    launch_context = g_object_new (test_launch_context_get_type (), NULL);
    1427  
    1428    switch (launch_data->type)
    1429      {
    1430      case TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_PATH_OVERRIDE:
    1431        old_path = g_strdup (g_getenv ("PATH"));
    1432        g_assert_true (g_setenv ("PATH", bin_path, TRUE));
    1433        break;
    1434  
    1435      case TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_CONTEXT:
    1436        g_app_launch_context_setenv (launch_context, "PATH", bin_path);
    1437        break;
    1438  
    1439      case TERMINAL_LAUNCH_TYPE_KEY_FILE_WITH_PATH:
    1440        g_app_launch_context_setenv (launch_context, "PATH", "/not/valid");
    1441        break;
    1442  
    1443      default:
    1444        g_assert_not_reached ();
    1445      }
    1446  
    1447    terminal_path = g_build_filename (bin_path, terminal_exec, NULL);
    1448    output_fd_path = g_build_filename (bin_path, "fifo", NULL);
    1449  
    1450    ret = mkfifo (output_fd_path, 0600);
    1451    g_assert_cmpint (ret, ==, 0);
    1452  
    1453    fd = g_open (output_fd_path, O_RDONLY | O_CLOEXEC | O_NONBLOCK, 0);
    1454    g_assert_cmpint (fd, >=, 0);
    1455  
    1456    flags = fcntl (fd, F_GETFL);
    1457    g_assert_cmpint (flags, >=, 0);
    1458  
    1459    ret = fcntl (fd, F_SETFL,  flags & ~O_NONBLOCK);
    1460    g_assert_cmpint (ret, ==, 0);
    1461  
    1462    input_stream = g_unix_input_stream_new (fd, TRUE);
    1463    data_input_stream = g_data_input_stream_new (input_stream);
    1464    script_contents = g_strdup_printf ("#!%s\n" \
    1465                                       "out='%s'\n"
    1466                                       "printf '%%s\\n' \"$*\" > \"$out\"\n",
    1467                                       sh,
    1468                                       output_fd_path);
    1469    g_file_set_contents (terminal_path, script_contents, -1, &error);
    1470    g_assert_no_error (error);
    1471    g_assert_cmpint (g_chmod (terminal_path, 0500), ==, 0);
    1472  
    1473    g_test_message ("Fake '%s' terminal created as: %s", terminal_exec, terminal_path);
    1474  
    1475    command_line = g_strdup_printf ("true %s-argument", terminal_exec);
    1476  
    1477    if (launch_data->type == TERMINAL_LAUNCH_TYPE_KEY_FILE_WITH_PATH)
    1478      {
    1479        GKeyFile *key_file;
    1480        char *key_file_contents;
    1481        const char base_file[] =
    1482          "[Desktop Entry]\n"
    1483          "Type=Application\n"
    1484          "Name=terminal launched app\n"
    1485          "Terminal=true\n"
    1486          "Path=%s\n"
    1487          "Exec=%s\n";
    1488  
    1489        key_file = g_key_file_new ();
    1490        key_file_contents = g_strdup_printf (base_file, bin_path, command_line);
    1491  
    1492        g_assert_true (
    1493          g_key_file_load_from_data (key_file, key_file_contents, -1,
    1494                                     G_KEY_FILE_NONE, NULL));
    1495  
    1496        app_info = (GAppInfo*) g_desktop_app_info_new_from_keyfile (key_file);
    1497        g_assert_true (G_IS_DESKTOP_APP_INFO (app_info));
    1498        g_assert_true (
    1499          g_desktop_app_info_get_boolean (G_DESKTOP_APP_INFO (app_info), "Terminal"));
    1500  
    1501        g_key_file_unref (key_file);
    1502        g_free (key_file_contents);
    1503      }
    1504    else
    1505      {
    1506        app_info = g_app_info_create_from_commandline (command_line,
    1507                                                       "Test App on Terminal",
    1508                                                       G_APP_INFO_CREATE_NEEDS_TERMINAL |
    1509                                                       G_APP_INFO_CREATE_SUPPORTS_URIS,
    1510                                                       &error);
    1511        g_assert_no_error (error);
    1512      }
    1513  
    1514    paths = g_list_prepend (NULL, bin_path);
    1515    uris = g_list_prepend (NULL, g_filename_to_uri (bin_path, NULL, &error));
    1516    g_assert_no_error (error);
    1517  
    1518    paths = g_list_prepend (paths, (gpointer) g_get_user_data_dir ());
    1519    uris = g_list_append (uris, g_filename_to_uri (g_get_user_data_dir (), NULL, &error));
    1520    g_assert_no_error (error);
    1521  
    1522    g_assert_cmpint (g_list_length (paths), ==, 2);
    1523    g_app_info_launch_uris (app_info, uris, launch_context, &error);
    1524    g_assert_no_error (error);
    1525  
    1526    while (output_contents == NULL)
    1527      {
    1528        output_contents =
    1529          g_data_input_stream_read_upto (data_input_stream, "\n", 1, NULL, NULL, &error);
    1530        g_assert_no_error (error);
    1531  
    1532        if (output_contents == NULL)
    1533          g_usleep (G_USEC_PER_SEC / 10);
    1534      }
    1535    g_test_message ("'%s' called with arguments: '%s'", terminal_exec, output_contents);
    1536  
    1537    g_data_input_stream_read_byte (data_input_stream, NULL, &error);
    1538    g_assert_no_error (error);
    1539  
    1540    output_args = g_strsplit (output_contents, " ", -1);
    1541    g_clear_pointer (&output_contents, g_free);
    1542  
    1543    terminal_divider_arg_length = (get_terminal_divider (terminal_exec) != NULL) ? 1 : 0;
    1544    g_assert_cmpuint (g_strv_length (output_args), ==, 3 + terminal_divider_arg_length);
    1545    if (terminal_divider_arg_length == 1)
    1546      {
    1547        g_assert_cmpstr (output_args[0], ==, get_terminal_divider (terminal_exec));
    1548        g_assert_cmpstr (output_args[1], ==, "true");
    1549        g_assert_cmpstr (output_args[2], ==, command_line + 5);
    1550      }
    1551    else
    1552      {
    1553        g_assert_cmpstr (output_args[0], ==, "true");
    1554        g_assert_cmpstr (output_args[1], ==, command_line + 5);
    1555      }
    1556    paths = g_list_delete_link (paths,
    1557      g_list_find_custom (paths, output_args[2 + terminal_divider_arg_length], g_str_equal));
    1558    g_assert_cmpint (g_list_length (paths), ==, 1);
    1559    g_clear_pointer (&output_args, g_strfreev);
    1560  
    1561    while (output_contents == NULL)
    1562      {
    1563        output_contents =
    1564          g_data_input_stream_read_upto (data_input_stream, "\n", 1, NULL, NULL, &error);
    1565        g_assert_no_error (error);
    1566  
    1567        if (output_contents == NULL)
    1568          g_usleep (G_USEC_PER_SEC / 10);
    1569      }
    1570    g_test_message ("'%s' called with arguments: '%s'", terminal_exec, output_contents);
    1571  
    1572    g_data_input_stream_read_byte (data_input_stream, NULL, &error);
    1573    g_assert_no_error (error);
    1574  
    1575    output_args = g_strsplit (output_contents, " ", -1);
    1576    g_clear_pointer (&output_contents, g_free);
    1577    g_assert_cmpuint (g_strv_length (output_args), ==, 3 + terminal_divider_arg_length);
    1578    if (terminal_divider_arg_length > 0)
    1579      {
    1580        g_assert_cmpstr (output_args[0], ==, get_terminal_divider (terminal_exec));
    1581        g_assert_cmpstr (output_args[1], ==, "true");
    1582        g_assert_cmpstr (output_args[2], ==, command_line + 5);
    1583      }
    1584    else
    1585      {
    1586        g_assert_cmpstr (output_args[0], ==, "true");
    1587        g_assert_cmpstr (output_args[1], ==, command_line + 5);
    1588      }
    1589    paths = g_list_delete_link (paths,
    1590      g_list_find_custom (paths, output_args[2 + terminal_divider_arg_length], g_str_equal));
    1591    g_assert_cmpint (g_list_length (paths), ==, 0);
    1592    g_clear_pointer (&output_args, g_strfreev);
    1593  
    1594    g_assert_null (paths);
    1595  
    1596    if (launch_data->type == TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_PATH_OVERRIDE)
    1597      g_assert_true (g_setenv ("PATH", old_path, TRUE));
    1598  
    1599    g_close (fd, &error);
    1600    g_assert_no_error (error);
    1601  
    1602    g_free (sh);
    1603    g_free (command_line);
    1604    g_free (bin_path);
    1605    g_free (terminal_path);
    1606    g_free (output_fd_path);
    1607    g_free (script_contents);
    1608    g_free (old_path);
    1609    g_clear_pointer (&output_args, g_strfreev);
    1610    g_clear_pointer (&output_contents, g_free);
    1611    g_clear_object (&data_input_stream);
    1612    g_clear_object (&input_stream);
    1613    g_clear_object (&app_info);
    1614    g_clear_object (&launch_context);
    1615    g_clear_error (&error);
    1616    g_clear_list (&paths, NULL);
    1617    g_clear_list (&uris, g_free);
    1618  }
    1619  
    1620  static void
    1621  test_launch_uris_with_invalid_terminal (void)
    1622  {
    1623    char *old_path;
    1624    char *bin_path;
    1625    GAppInfo *app_info;
    1626    GError *error = NULL;
    1627  
    1628    bin_path = g_dir_make_tmp ("bin-path-XXXXXX", &error);
    1629    g_assert_no_error (error);
    1630  
    1631    old_path = g_strdup (g_getenv ("PATH"));
    1632    g_assert_true (g_setenv ("PATH", bin_path, TRUE));
    1633  
    1634    app_info = g_app_info_create_from_commandline ("true invalid-glib-terminal",
    1635                                                   "Test App on Invalid Terminal",
    1636                                                   G_APP_INFO_CREATE_NEEDS_TERMINAL |
    1637                                                   G_APP_INFO_CREATE_SUPPORTS_URIS,
    1638                                                   &error);
    1639    g_assert_no_error (error);
    1640  
    1641    g_app_info_launch_uris (app_info, NULL, NULL, &error);
    1642    g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
    1643    g_clear_error (&error);
    1644  
    1645    g_assert_true (g_setenv ("PATH", old_path, TRUE));
    1646  
    1647    g_clear_object (&app_info);
    1648    g_clear_error (&error);
    1649    g_free (bin_path);
    1650    g_free (old_path);
    1651  }
    1652  
    1653  static void
    1654  test_app_path (void)
    1655  {
    1656    GDesktopAppInfo *appinfo;
    1657    const char *desktop_path;
    1658  
    1659    desktop_path = g_test_get_filename (G_TEST_BUILT, "appinfo-test-path.desktop", NULL);
    1660    appinfo = g_desktop_app_info_new_from_filename (desktop_path);
    1661  
    1662    g_assert_true (G_IS_DESKTOP_APP_INFO (appinfo));
    1663  
    1664    g_clear_object (&appinfo);
    1665  }
    1666  
    1667  static void
    1668  test_app_path_wrong (void)
    1669  {
    1670    GKeyFile *key_file;
    1671    GDesktopAppInfo *appinfo;
    1672    const gchar bad_try_exec_file_contents[] =
    1673      "[Desktop Entry]\n"
    1674      "Type=Application\n"
    1675      "Name=appinfo-test\n"
    1676      "TryExec=appinfo-test\n"
    1677      "Path=this-must-not-exist‼\n"
    1678      "Exec=true\n";
    1679    const gchar bad_exec_file_contents[] =
    1680      "[Desktop Entry]\n"
    1681      "Type=Application\n"
    1682      "Name=appinfo-test\n"
    1683      "TryExec=true\n"
    1684      "Path=this-must-not-exist‼\n"
    1685      "Exec=appinfo-test\n";
    1686  
    1687    g_assert_true (
    1688      g_file_test (g_test_get_filename (G_TEST_BUILT, "appinfo-test", NULL),
    1689        G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE));
    1690  
    1691    key_file = g_key_file_new ();
    1692  
    1693    g_assert_true (
    1694      g_key_file_load_from_data (key_file, bad_try_exec_file_contents, -1,
    1695                                 G_KEY_FILE_NONE, NULL));
    1696  
    1697    appinfo = g_desktop_app_info_new_from_keyfile (key_file);
    1698    g_assert_false (G_IS_DESKTOP_APP_INFO (appinfo));
    1699  
    1700    g_assert_true (
    1701      g_key_file_load_from_data (key_file, bad_exec_file_contents, -1,
    1702                                 G_KEY_FILE_NONE, NULL));
    1703  
    1704    appinfo = g_desktop_app_info_new_from_keyfile (key_file);
    1705    g_assert_false (G_IS_DESKTOP_APP_INFO (appinfo));
    1706  
    1707    g_clear_pointer (&key_file, g_key_file_unref);
    1708    g_clear_object (&appinfo);
    1709  }
    1710  
    1711  static void
    1712  test_launch_startup_notify_fail (void)
    1713  {
    1714    GAppInfo *app_info;
    1715    GAppLaunchContext *context;
    1716    GError *error = NULL;
    1717    gboolean launch_started;
    1718    gboolean launch_failed;
    1719    gboolean launched;
    1720    GList *uris;
    1721  
    1722    app_info = g_app_info_create_from_commandline ("this-must-not-exist‼",
    1723                                                   "failing app",
    1724                                                   G_APP_INFO_CREATE_NONE |
    1725                                                   G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
    1726                                                   &error);
    1727    g_assert_no_error (error);
    1728  
    1729    context = g_object_new (test_launch_context_get_type (), NULL);
    1730    g_signal_connect (context, "launch-started",
    1731                      G_CALLBACK (on_launch_started),
    1732                      &launch_started);
    1733    g_signal_connect (context, "launched",
    1734                      G_CALLBACK (on_launch_started),
    1735                      &launched);
    1736    g_signal_connect (context, "launch-failed",
    1737                      G_CALLBACK (on_launch_failed),
    1738                      &launch_failed);
    1739  
    1740    launch_started = FALSE;
    1741    launch_failed = FALSE;
    1742    launched = FALSE;
    1743    uris = g_list_prepend (NULL, g_file_new_for_uri ("foo://bar"));
    1744    uris = g_list_prepend (uris, g_file_new_for_uri ("bar://foo"));
    1745    g_assert_false (g_app_info_launch (app_info, uris, context, &error));
    1746    g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
    1747    g_assert_true (launch_started);
    1748    g_assert_true (launch_failed);
    1749    g_assert_false (launched);
    1750  
    1751    g_clear_error (&error);
    1752    g_clear_object (&app_info);
    1753    g_clear_object (&context);
    1754    g_clear_list (&uris, g_object_unref);
    1755  }
    1756  
    1757  static void
    1758  test_launch_fail (void)
    1759  {
    1760    GAppInfo *app_info;
    1761    GError *error = NULL;
    1762  
    1763    app_info = g_app_info_create_from_commandline ("this-must-not-exist‼",
    1764                                                   "failing app",
    1765                                                   G_APP_INFO_CREATE_NONE,
    1766                                                   &error);
    1767    g_assert_no_error (error);
    1768  
    1769    g_assert_false (g_app_info_launch (app_info, NULL, NULL, &error));
    1770    g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
    1771  
    1772    g_clear_error (&error);
    1773    g_clear_object (&app_info);
    1774  }
    1775  
    1776  static void
    1777  test_launch_fail_absolute_path (void)
    1778  {
    1779    GAppInfo *app_info;
    1780    GError *error = NULL;
    1781  
    1782    app_info = g_app_info_create_from_commandline ("/nothing/of/this-must-exist‼",
    1783                                                   NULL,
    1784                                                   G_APP_INFO_CREATE_NONE,
    1785                                                   &error);
    1786    g_assert_no_error (error);
    1787  
    1788    g_assert_false (g_app_info_launch (app_info, NULL, NULL, &error));
    1789    g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
    1790  
    1791    g_clear_error (&error);
    1792    g_clear_object (&app_info);
    1793  
    1794    app_info = g_app_info_create_from_commandline ("/",
    1795                                                   NULL,
    1796                                                   G_APP_INFO_CREATE_NONE,
    1797                                                   &error);
    1798    g_assert_no_error (error);
    1799  
    1800    g_assert_false (g_app_info_launch (app_info, NULL, NULL, &error));
    1801    g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
    1802  
    1803    g_clear_error (&error);
    1804    g_clear_object (&app_info);
    1805  }
    1806  
    1807  static void
    1808  async_result_cb (GObject      *source_object,
    1809                   GAsyncResult *result,
    1810                   gpointer      user_data)
    1811  {
    1812    GAsyncResult **result_out = user_data;
    1813  
    1814    g_assert (*result_out == NULL);
    1815    *result_out = g_object_ref (result);
    1816    g_main_context_wakeup (g_main_context_get_thread_default ());
    1817  }
    1818  
    1819  static void
    1820  test_launch_fail_dbus (void)
    1821  {
    1822    GTestDBus *bus = NULL;
    1823    GDesktopAppInfo *app_info = NULL;
    1824    GAppLaunchContext *context = NULL;
    1825    GAsyncResult *result = NULL;
    1826    GError *error = NULL;
    1827  
    1828    /* Set up a test session bus to ensure that launching the app happens using
    1829     * D-Bus rather than spawning. */
    1830    bus = g_test_dbus_new (G_TEST_DBUS_NONE);
    1831    g_test_dbus_up (bus);
    1832  
    1833    app_info = g_desktop_app_info_new_from_filename (g_test_get_filename (G_TEST_DIST, "org.gtk.test.dbusappinfo.desktop", NULL));
    1834    g_assert_nonnull (app_info);
    1835  
    1836    g_assert_true (g_desktop_app_info_has_key (app_info, "DBusActivatable"));
    1837  
    1838    context = g_app_launch_context_new ();
    1839  
    1840    g_app_info_launch_uris_async (G_APP_INFO (app_info), NULL, context, NULL, async_result_cb, &result);
    1841  
    1842    while (result == NULL)
    1843      g_main_context_iteration (NULL, TRUE);
    1844  
    1845    g_assert_false (g_app_info_launch_uris_finish (G_APP_INFO (app_info), result, &error));
    1846    g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN);
    1847  
    1848    g_test_dbus_down (bus);
    1849    g_clear_object (&bus);
    1850  
    1851    g_clear_error (&error);
    1852    g_clear_object (&result);
    1853    g_clear_object (&context);
    1854    g_clear_object (&app_info);
    1855  }
    1856  
    1857  int
    1858  main (int   argc,
    1859        char *argv[])
    1860  {
    1861    guint i;
    1862    const gchar *supported_terminals[] = {
    1863      "xdg-terminal-exec",
    1864      "kgx",
    1865      "gnome-terminal",
    1866      "mate-terminal",
    1867      "xfce4-terminal",
    1868      "tilix",
    1869      "konsole",
    1870      "nxterm",
    1871      "color-xterm",
    1872      "rxvt",
    1873      "dtterm",
    1874      "xterm",
    1875    };
    1876  
    1877    /* While we use %G_TEST_OPTION_ISOLATE_DIRS to create temporary directories
    1878     * for each of the tests, we want to use the system MIME registry, assuming
    1879     * that it exists and correctly has shared-mime-info installed. */
    1880    g_content_type_set_mime_dirs (NULL);
    1881  
    1882    g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
    1883  
    1884    g_test_add_func ("/desktop-app-info/delete", test_delete);
    1885    g_test_add_func ("/desktop-app-info/default", test_default);
    1886    g_test_add_func ("/desktop-app-info/default-async", test_default_async);
    1887    g_test_add_func ("/desktop-app-info/fallback", test_fallback);
    1888    g_test_add_func ("/desktop-app-info/lastused", test_last_used);
    1889    g_test_add_func ("/desktop-app-info/extra-getters", test_extra_getters);
    1890    g_test_add_func ("/desktop-app-info/actions", test_actions);
    1891    g_test_add_func ("/desktop-app-info/search", test_search);
    1892    g_test_add_func ("/desktop-app-info/implements", test_implements);
    1893    g_test_add_func ("/desktop-app-info/show-in", test_show_in);
    1894    g_test_add_func ("/desktop-app-info/app-path", test_app_path);
    1895    g_test_add_func ("/desktop-app-info/app-path/wrong", test_app_path_wrong);
    1896    g_test_add_func ("/desktop-app-info/launch/fail", test_launch_fail);
    1897    g_test_add_func ("/desktop-app-info/launch/fail-absolute-path", test_launch_fail_absolute_path);
    1898    g_test_add_func ("/desktop-app-info/launch/fail-startup-notify", test_launch_startup_notify_fail);
    1899    g_test_add_func ("/desktop-app-info/launch/fail-dbus", test_launch_fail_dbus);
    1900    g_test_add_func ("/desktop-app-info/launch-as-manager", test_launch_as_manager);
    1901    g_test_add_func ("/desktop-app-info/launch-as-manager/fail", test_launch_as_manager_fail);
    1902    g_test_add_func ("/desktop-app-info/launch-default-uri-handler", test_default_uri_handler);
    1903    g_test_add_func ("/desktop-app-info/launch-default-uri-handler-async", test_default_uri_handler_async);
    1904    g_test_add_func ("/desktop-app-info/id", test_id);
    1905  
    1906    for (i = 0; i < G_N_ELEMENTS (supported_terminals); i++)
    1907      {
    1908        char *path;
    1909  
    1910        path = g_strdup_printf ("/desktop-app-info/launch-uris-with-terminal/with-path/%s",
    1911                                supported_terminals[i]);
    1912        g_test_add_data_func_full (path,
    1913                                   terminal_launch_data_new (supported_terminals[i],
    1914                                                             TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_PATH_OVERRIDE),
    1915                                   test_launch_uris_with_terminal, g_free);
    1916        g_clear_pointer (&path, g_free);
    1917  
    1918        path = g_strdup_printf ("/desktop-app-info/launch-uris-with-terminal/with-context/%s",
    1919                                supported_terminals[i]);
    1920        g_test_add_data_func_full (path,
    1921                                   terminal_launch_data_new (supported_terminals[i],
    1922                                                             TERMINAL_LAUNCH_TYPE_COMMAND_LINE_WITH_CONTEXT),
    1923                                   test_launch_uris_with_terminal, g_free);
    1924        g_clear_pointer (&path, g_free);
    1925  
    1926        path = g_strdup_printf ("/desktop-app-info/launch-uris-with-terminal/with-desktop-path/%s",
    1927                                supported_terminals[i]);
    1928        g_test_add_data_func_full (path,
    1929                                   terminal_launch_data_new (supported_terminals[i],
    1930                                                             TERMINAL_LAUNCH_TYPE_KEY_FILE_WITH_PATH),
    1931                                   test_launch_uris_with_terminal, g_free);
    1932        g_clear_pointer (&path, g_free);
    1933      }
    1934  
    1935    g_test_add_func ("/desktop-app-info/launch-uris-with-terminal/invalid-glib-terminal",
    1936                     test_launch_uris_with_invalid_terminal);
    1937  
    1938    return g_test_run ();
    1939  }