(root)/
glib-2.79.0/
gio/
tests/
dbus-appinfo.c
       1  /*
       2   * Copyright © 2013 Canonical Limited
       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   * Authors: Ryan Lortie <desrt@desrt.ca>
      20   */
      21  
      22  #include <gio/gio.h>
      23  #include <gio/gdesktopappinfo.h>
      24  
      25  #include "gdbus-sessionbus.h"
      26  
      27  static GDesktopAppInfo *appinfo;
      28  static int current_state;
      29  static gboolean saw_startup_id;
      30  static gboolean requested_startup_id;
      31  
      32  
      33  static GType test_app_launch_context_get_type (void);
      34  typedef GAppLaunchContext TestAppLaunchContext;
      35  typedef GAppLaunchContextClass TestAppLaunchContextClass;
      36  G_DEFINE_TYPE (TestAppLaunchContext, test_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
      37  
      38  static gchar *
      39  test_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
      40                                                 GAppInfo          *info,
      41                                                 GList             *uris)
      42  {
      43    requested_startup_id = TRUE;
      44    return g_strdup ("expected startup id");
      45  }
      46  
      47  static void
      48  test_app_launch_context_init (TestAppLaunchContext *ctx)
      49  {
      50  }
      51  
      52  static void
      53  test_app_launch_context_class_init (GAppLaunchContextClass *class)
      54  {
      55    class->get_startup_notify_id = test_app_launch_context_get_startup_notify_id;
      56  }
      57  
      58  static GType test_application_get_type (void);
      59  typedef GApplication TestApplication;
      60  typedef GApplicationClass TestApplicationClass;
      61  G_DEFINE_TYPE (TestApplication, test_application, G_TYPE_APPLICATION)
      62  
      63  static void
      64  saw_action (const gchar *action)
      65  {
      66    /* This is the main driver of the test.  It's a bit of a state
      67     * machine.
      68     *
      69     * Each time some event arrives on the app, it calls here to report
      70     * which event it was.  The initial activation of the app is what
      71     * starts everything in motion (starting from state 0).  At each
      72     * state, we assert that we receive the expected event, send the next
      73     * event, then update the current_state variable so we do the correct
      74     * thing next time.
      75     */
      76  
      77    switch (current_state)
      78      {
      79        case 0: g_assert_cmpstr (action, ==, "activate");
      80  
      81        /* Let's try another activation... */
      82        g_app_info_launch (G_APP_INFO (appinfo), NULL, NULL, NULL);
      83        current_state = 1; return; case 1: g_assert_cmpstr (action, ==, "activate");
      84  
      85  
      86        /* Now let's try opening some files... */
      87        {
      88          GList *files;
      89  
      90          files = g_list_prepend (NULL, g_file_new_for_uri ("file:///a/b"));
      91          files = g_list_append (files, g_file_new_for_uri ("file:///c/d"));
      92          g_app_info_launch (G_APP_INFO (appinfo), files, NULL, NULL);
      93          g_list_free_full (files, g_object_unref);
      94        }
      95        current_state = 2; return; case 2: g_assert_cmpstr (action, ==, "open");
      96  
      97        /* Now action activations... */
      98        g_desktop_app_info_launch_action (appinfo, "frob", NULL);
      99        current_state = 3; return; case 3: g_assert_cmpstr (action, ==, "frob");
     100  
     101        g_desktop_app_info_launch_action (appinfo, "tweak", NULL);
     102        current_state = 4; return; case 4: g_assert_cmpstr (action, ==, "tweak");
     103  
     104        g_desktop_app_info_launch_action (appinfo, "twiddle", NULL);
     105        current_state = 5; return; case 5: g_assert_cmpstr (action, ==, "twiddle");
     106  
     107        /* Now launch the app with startup notification */
     108        {
     109          GAppLaunchContext *ctx;
     110  
     111          g_assert (saw_startup_id == FALSE);
     112          ctx = g_object_new (test_app_launch_context_get_type (), NULL);
     113          g_app_info_launch (G_APP_INFO (appinfo), NULL, ctx, NULL);
     114          g_assert (requested_startup_id);
     115          requested_startup_id = FALSE;
     116          g_object_unref (ctx);
     117        }
     118        current_state = 6; return; case 6: g_assert_cmpstr (action, ==, "activate"); g_assert (saw_startup_id);
     119        saw_startup_id = FALSE;
     120  
     121        /* Now do the same for an action */
     122        {
     123          GAppLaunchContext *ctx;
     124  
     125          g_assert (saw_startup_id == FALSE);
     126          ctx = g_object_new (test_app_launch_context_get_type (), NULL);
     127          g_desktop_app_info_launch_action (appinfo, "frob", ctx);
     128          g_assert (requested_startup_id);
     129          requested_startup_id = FALSE;
     130          g_object_unref (ctx);
     131        }
     132        current_state = 7; return; case 7: g_assert_cmpstr (action, ==, "frob"); g_assert (saw_startup_id);
     133        saw_startup_id = FALSE;
     134  
     135        /* Now quit... */
     136        g_desktop_app_info_launch_action (appinfo, "quit", NULL);
     137        current_state = 8; return; case 8: g_assert_not_reached ();
     138      }
     139  }
     140  
     141  static void
     142  test_application_frob (GSimpleAction *action,
     143                         GVariant      *parameter,
     144                         gpointer       user_data)
     145  {
     146    g_assert (parameter == NULL);
     147    saw_action ("frob");
     148  }
     149  
     150  static void
     151  test_application_tweak (GSimpleAction *action,
     152                          GVariant      *parameter,
     153                          gpointer       user_data)
     154  {
     155    g_assert (parameter == NULL);
     156    saw_action ("tweak");
     157  }
     158  
     159  static void
     160  test_application_twiddle (GSimpleAction *action,
     161                            GVariant      *parameter,
     162                            gpointer       user_data)
     163  {
     164    g_assert (parameter == NULL);
     165    saw_action ("twiddle");
     166  }
     167  
     168  static void
     169  test_application_quit (GSimpleAction *action,
     170                         GVariant      *parameter,
     171                         gpointer       user_data)
     172  {
     173    GApplication *application = user_data;
     174  
     175    g_application_quit (application);
     176  }
     177  
     178  static const GActionEntry app_actions[] = {
     179    { "frob",         test_application_frob,    NULL, NULL, NULL, { 0 } },
     180    { "tweak",        test_application_tweak,   NULL, NULL, NULL, { 0 } },
     181    { "twiddle",      test_application_twiddle, NULL, NULL, NULL, { 0 } },
     182    { "quit",         test_application_quit,    NULL, NULL, NULL, { 0 } }
     183  };
     184  
     185  static void
     186  test_application_activate (GApplication *application)
     187  {
     188    /* Unbalanced, but that's OK because we will quit() */
     189    g_application_hold (application);
     190  
     191    saw_action ("activate");
     192  }
     193  
     194  static void
     195  test_application_open (GApplication  *application,
     196                         GFile        **files,
     197                         gint           n_files,
     198                         const gchar   *hint)
     199  {
     200    GFile *f;
     201  
     202    g_assert_cmpstr (hint, ==, "");
     203  
     204    g_assert_cmpint (n_files, ==, 2);
     205    f = g_file_new_for_uri ("file:///a/b");
     206    g_assert (g_file_equal (files[0], f));
     207    g_object_unref (f);
     208    f = g_file_new_for_uri ("file:///c/d");
     209    g_assert (g_file_equal (files[1], f));
     210    g_object_unref (f);
     211  
     212    saw_action ("open");
     213  }
     214  
     215  static void
     216  test_application_startup (GApplication *application)
     217  {
     218    G_APPLICATION_CLASS (test_application_parent_class)
     219      ->startup (application);
     220  
     221    g_action_map_add_action_entries (G_ACTION_MAP (application), app_actions, G_N_ELEMENTS (app_actions), application);
     222  }
     223  
     224  static void
     225  test_application_before_emit (GApplication *application,
     226                                GVariant     *platform_data)
     227  {
     228    const gchar *startup_id;
     229    gsize i;
     230  
     231    g_assert (!saw_startup_id);
     232  
     233    const gchar *startup_id_keys[] = {
     234      "desktop-startup-id",
     235      "activation-token",
     236      NULL,
     237    };
     238  
     239    for (i = 0; startup_id_keys[i] != NULL; i++)
     240      {
     241        if (!g_variant_lookup (platform_data, startup_id_keys[i], "&s", &startup_id))
     242          return;
     243  
     244        g_assert_cmpstr (startup_id, ==, "expected startup id");
     245      }
     246  
     247    saw_startup_id = TRUE;
     248  }
     249  
     250  static void
     251  test_application_init (TestApplication *app)
     252  {
     253  }
     254  
     255  static void
     256  test_application_class_init (GApplicationClass *class)
     257  {
     258    class->before_emit = test_application_before_emit;
     259    class->startup = test_application_startup;
     260    class->activate = test_application_activate;
     261    class->open = test_application_open;
     262  }
     263  
     264  static void
     265  test_dbus_appinfo (void)
     266  {
     267    const gchar *argv[] = { "myapp", NULL };
     268    TestApplication *app;
     269    int status;
     270    gchar *desktop_file = NULL;
     271  
     272    desktop_file = g_test_build_filename (G_TEST_DIST,
     273                                          "org.gtk.test.dbusappinfo.desktop",
     274                                          NULL);
     275    appinfo = g_desktop_app_info_new_from_filename (desktop_file);
     276    g_assert (appinfo != NULL);
     277    g_free (desktop_file);
     278  
     279    app = g_object_new (test_application_get_type (),
     280                        "application-id", "org.gtk.test.dbusappinfo",
     281                        "flags", G_APPLICATION_HANDLES_OPEN,
     282                        NULL);
     283    status = g_application_run (app, 1, (gchar **) argv);
     284  
     285    g_assert_cmpint (status, ==, 0);
     286    g_assert_cmpint (current_state, ==, 8);
     287  
     288    g_object_unref (appinfo);
     289    g_object_unref (app);
     290  }
     291  
     292  static void
     293  on_flatpak_launch_uris_finish (GObject *object,
     294                                 GAsyncResult *result,
     295                                 gpointer user_data)
     296  {
     297    GApplication *app = user_data;
     298    GError *error = NULL;
     299  
     300    g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
     301    g_assert_no_error (error);
     302  
     303    g_application_release (app);
     304  }
     305  
     306  static void
     307  on_flatpak_activate (GApplication *app,
     308                       gpointer user_data)
     309  {
     310    GDesktopAppInfo *flatpak_appinfo = user_data;
     311    char *uri;
     312    GList *uris;
     313  
     314    /* The app will be released in on_flatpak_launch_uris_finish */
     315    g_application_hold (app);
     316  
     317    uri = g_filename_to_uri (g_desktop_app_info_get_filename (flatpak_appinfo), NULL, NULL);
     318    g_assert_nonnull (uri);
     319    uris = g_list_prepend (NULL, uri);
     320    g_app_info_launch_uris_async (G_APP_INFO (flatpak_appinfo), uris, NULL,
     321                                  NULL, on_flatpak_launch_uris_finish, app);
     322    g_list_free (uris);
     323    g_free (uri);
     324  }
     325  
     326  static void
     327  on_flatpak_open (GApplication  *app,
     328                   GFile        **files,
     329                   gint           n_files,
     330                   const char    *hint)
     331  {
     332    GFile *f;
     333  
     334    g_assert_cmpint (n_files, ==, 1);
     335    g_test_message ("on_flatpak_open received file '%s'", g_file_peek_path (files[0]));
     336  
     337    /* The file has been exported via the document portal */
     338    f = g_file_new_for_uri ("file:///document-portal/document-id/org.gtk.test.dbusappinfo.flatpak.desktop");
     339    g_assert_true (g_file_equal (files[0], f));
     340    g_object_unref (f);
     341  }
     342  
     343  static void
     344  test_flatpak_doc_export (void)
     345  {
     346    const gchar *argv[] = { "myapp", NULL };
     347    gchar *desktop_file = NULL;
     348    GDesktopAppInfo *flatpak_appinfo;
     349    GApplication *app;
     350    int status;
     351  
     352    g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal.");
     353  
     354    desktop_file = g_test_build_filename (G_TEST_DIST,
     355                                          "org.gtk.test.dbusappinfo.flatpak.desktop",
     356                                          NULL);
     357    flatpak_appinfo = g_desktop_app_info_new_from_filename (desktop_file);
     358    g_assert_nonnull (flatpak_appinfo);
     359    g_free (desktop_file);
     360  
     361    app = g_application_new ("org.gtk.test.dbusappinfo.flatpak",
     362                             G_APPLICATION_HANDLES_OPEN);
     363    g_signal_connect (app, "activate", G_CALLBACK (on_flatpak_activate),
     364                      flatpak_appinfo);
     365    g_signal_connect (app, "open", G_CALLBACK (on_flatpak_open), NULL);
     366  
     367    status = g_application_run (app, 1, (gchar **) argv);
     368    g_assert_cmpint (status, ==, 0);
     369  
     370    g_object_unref (app);
     371    g_object_unref (flatpak_appinfo);
     372  }
     373  
     374  static void
     375  on_flatpak_launch_invalid_uri_finish (GObject *object,
     376                                        GAsyncResult *result,
     377                                        gpointer user_data)
     378  {
     379    GApplication *app = user_data;
     380    GError *error = NULL;
     381  
     382    g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
     383    g_assert_no_error (error);
     384  
     385    g_application_release (app);
     386  }
     387  
     388  static void
     389  on_flatpak_activate_invalid_uri (GApplication *app,
     390                                   gpointer user_data)
     391  {
     392    GDesktopAppInfo *flatpak_appinfo = user_data;
     393    GList *uris;
     394  
     395    /* The app will be released in on_flatpak_launch_uris_finish */
     396    g_application_hold (app);
     397  
     398    uris = g_list_prepend (NULL, "file:///hopefully/an/invalid/path.desktop");
     399    g_app_info_launch_uris_async (G_APP_INFO (flatpak_appinfo), uris, NULL,
     400                                  NULL, on_flatpak_launch_invalid_uri_finish, app);
     401    g_list_free (uris);
     402  }
     403  
     404  static void
     405  on_flatpak_open_invalid_uri (GApplication  *app,
     406                               GFile        **files,
     407                               gint           n_files,
     408                               const char    *hint)
     409  {
     410    GFile *f;
     411  
     412    g_assert_cmpint (n_files, ==, 1);
     413    g_test_message ("on_flatpak_open received file '%s'", g_file_peek_path (files[0]));
     414  
     415    /* The file has been exported via the document portal */
     416    f = g_file_new_for_uri ("file:///hopefully/an/invalid/path.desktop");
     417    g_assert_true (g_file_equal (files[0], f));
     418    g_object_unref (f);
     419  }
     420  
     421  static void
     422  test_flatpak_missing_doc_export (void)
     423  {
     424    const gchar *argv[] = { "myapp", NULL };
     425    gchar *desktop_file = NULL;
     426    GDesktopAppInfo *flatpak_appinfo;
     427    GApplication *app;
     428    int status;
     429  
     430    g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal.");
     431  
     432    desktop_file = g_test_build_filename (G_TEST_DIST,
     433                                          "org.gtk.test.dbusappinfo.flatpak.desktop",
     434                                          NULL);
     435    flatpak_appinfo = g_desktop_app_info_new_from_filename (desktop_file);
     436    g_assert_nonnull (flatpak_appinfo);
     437  
     438    app = g_application_new ("org.gtk.test.dbusappinfo.flatpak",
     439                             G_APPLICATION_HANDLES_OPEN);
     440    g_signal_connect (app, "activate", G_CALLBACK (on_flatpak_activate_invalid_uri),
     441                      flatpak_appinfo);
     442    g_signal_connect (app, "open", G_CALLBACK (on_flatpak_open_invalid_uri), NULL);
     443  
     444    status = g_application_run (app, 1, (gchar **) argv);
     445    g_assert_cmpint (status, ==, 0);
     446  
     447    g_object_unref (app);
     448    g_object_unref (flatpak_appinfo);
     449    g_free (desktop_file);
     450  }
     451  
     452  int
     453  main (int argc, char **argv)
     454  {
     455    g_test_init (&argc, &argv, NULL);
     456  
     457    g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo);
     458    g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export);
     459    g_test_add_func ("/appinfo/flatpak-missing-doc-export", test_flatpak_missing_doc_export);
     460  
     461    return session_bus_run ();
     462  }