gkdbus: Fix underflow and unreachable code bug
[platform/upstream/glib.git] / 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 }