Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2013 Canonical Limited
3 : : * Copyright © 2024 GNOME Foundation Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Authors: Ryan Lortie <desrt@desrt.ca>
21 : : * Julian Sparber <jsparber@gnome.org>
22 : : */
23 : :
24 : : #include <gio/gio.h>
25 : : #include <gio/gdesktopappinfo.h>
26 : :
27 : : #include "gdbus-sessionbus.h"
28 : : #include "fake-desktop-portal.h"
29 : : #include "fake-document-portal.h"
30 : :
31 : : static GDesktopAppInfo *appinfo;
32 : : static int current_state;
33 : : static gboolean saw_startup_id;
34 : : static gboolean requested_startup_id;
35 : :
36 : :
37 : : static GType test_app_launch_context_get_type (void);
38 : : typedef GAppLaunchContext TestAppLaunchContext;
39 : : typedef GAppLaunchContextClass TestAppLaunchContextClass;
40 : 12 : G_DEFINE_TYPE (TestAppLaunchContext, test_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
41 : :
42 : : static gchar *
43 : 10 : test_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
44 : : GAppInfo *info,
45 : : GList *uris)
46 : : {
47 : 10 : requested_startup_id = TRUE;
48 : 10 : return g_strdup ("expected startup id");
49 : : }
50 : :
51 : : static void
52 : 10 : test_app_launch_context_init (TestAppLaunchContext *ctx)
53 : : {
54 : 10 : }
55 : :
56 : : static void
57 : 1 : test_app_launch_context_class_init (GAppLaunchContextClass *class)
58 : : {
59 : 1 : class->get_startup_notify_id = test_app_launch_context_get_startup_notify_id;
60 : 1 : }
61 : :
62 : : static GType test_application_get_type (void);
63 : : typedef GApplication TestApplication;
64 : : typedef GApplicationClass TestApplicationClass;
65 : 3 : G_DEFINE_TYPE (TestApplication, test_application, G_TYPE_APPLICATION)
66 : :
67 : : static void
68 : 8 : saw_action (const gchar *action)
69 : : {
70 : : /* This is the main driver of the test. It's a bit of a state
71 : : * machine.
72 : : *
73 : : * Each time some event arrives on the app, it calls here to report
74 : : * which event it was. The initial activation of the app is what
75 : : * starts everything in motion (starting from state 0). At each
76 : : * state, we assert that we receive the expected event, send the next
77 : : * event, then update the current_state variable so we do the correct
78 : : * thing next time.
79 : : */
80 : :
81 : 8 : switch (current_state)
82 : : {
83 : 1 : case 0: g_assert_cmpstr (action, ==, "activate");
84 : :
85 : : /* Let's try another activation... */
86 : 1 : g_app_info_launch (G_APP_INFO (appinfo), NULL, NULL, NULL);
87 : 2 : current_state = 1; return; case 1: g_assert_cmpstr (action, ==, "activate");
88 : :
89 : :
90 : : /* Now let's try opening some files... */
91 : : {
92 : : GList *files;
93 : :
94 : 1 : files = g_list_prepend (NULL, g_file_new_for_uri ("file:///a/b"));
95 : 1 : files = g_list_append (files, g_file_new_for_uri ("file:///c/d"));
96 : 1 : g_app_info_launch (G_APP_INFO (appinfo), files, NULL, NULL);
97 : 1 : g_list_free_full (files, g_object_unref);
98 : : }
99 : 2 : current_state = 2; return; case 2: g_assert_cmpstr (action, ==, "open");
100 : :
101 : : /* Now action activations... */
102 : 1 : g_desktop_app_info_launch_action (appinfo, "frob", NULL);
103 : 2 : current_state = 3; return; case 3: g_assert_cmpstr (action, ==, "frob");
104 : :
105 : 1 : g_desktop_app_info_launch_action (appinfo, "tweak", NULL);
106 : 2 : current_state = 4; return; case 4: g_assert_cmpstr (action, ==, "tweak");
107 : :
108 : 1 : g_desktop_app_info_launch_action (appinfo, "twiddle", NULL);
109 : 2 : current_state = 5; return; case 5: g_assert_cmpstr (action, ==, "twiddle");
110 : :
111 : : /* Now launch the app with startup notification */
112 : : {
113 : : GAppLaunchContext *ctx;
114 : :
115 : 1 : g_assert (saw_startup_id == FALSE);
116 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
117 : 1 : g_app_info_launch (G_APP_INFO (appinfo), NULL, ctx, NULL);
118 : 1 : g_assert (requested_startup_id);
119 : 1 : requested_startup_id = FALSE;
120 : 1 : g_object_unref (ctx);
121 : : }
122 : 2 : current_state = 6; return; case 6: g_assert_cmpstr (action, ==, "activate"); g_assert (saw_startup_id);
123 : 1 : saw_startup_id = FALSE;
124 : :
125 : : /* Now do the same for an action */
126 : : {
127 : : GAppLaunchContext *ctx;
128 : :
129 : 1 : g_assert (saw_startup_id == FALSE);
130 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
131 : 1 : g_desktop_app_info_launch_action (appinfo, "frob", ctx);
132 : 1 : g_assert (requested_startup_id);
133 : 1 : requested_startup_id = FALSE;
134 : 1 : g_object_unref (ctx);
135 : : }
136 : 2 : current_state = 7; return; case 7: g_assert_cmpstr (action, ==, "frob"); g_assert (saw_startup_id);
137 : 1 : saw_startup_id = FALSE;
138 : :
139 : : /* Now quit... */
140 : 1 : g_desktop_app_info_launch_action (appinfo, "quit", NULL);
141 : : current_state = 8; return; case 8: g_assert_not_reached ();
142 : : }
143 : : }
144 : :
145 : : static void
146 : 2 : test_application_frob (GSimpleAction *action,
147 : : GVariant *parameter,
148 : : gpointer user_data)
149 : : {
150 : 2 : g_assert (parameter == NULL);
151 : 2 : saw_action ("frob");
152 : 2 : }
153 : :
154 : : static void
155 : 1 : test_application_tweak (GSimpleAction *action,
156 : : GVariant *parameter,
157 : : gpointer user_data)
158 : : {
159 : 1 : g_assert (parameter == NULL);
160 : 1 : saw_action ("tweak");
161 : 1 : }
162 : :
163 : : static void
164 : 1 : test_application_twiddle (GSimpleAction *action,
165 : : GVariant *parameter,
166 : : gpointer user_data)
167 : : {
168 : 1 : g_assert (parameter == NULL);
169 : 1 : saw_action ("twiddle");
170 : 1 : }
171 : :
172 : : static void
173 : 1 : test_application_quit (GSimpleAction *action,
174 : : GVariant *parameter,
175 : : gpointer user_data)
176 : : {
177 : 1 : GApplication *application = user_data;
178 : :
179 : 1 : g_application_quit (application);
180 : 1 : }
181 : :
182 : : static const GActionEntry app_actions[] = {
183 : : { "frob", test_application_frob, NULL, NULL, NULL, { 0 } },
184 : : { "tweak", test_application_tweak, NULL, NULL, NULL, { 0 } },
185 : : { "twiddle", test_application_twiddle, NULL, NULL, NULL, { 0 } },
186 : : { "quit", test_application_quit, NULL, NULL, NULL, { 0 } }
187 : : };
188 : :
189 : : static void
190 : 3 : test_application_activate (GApplication *application)
191 : : {
192 : : /* Unbalanced, but that's OK because we will quit() */
193 : 3 : g_application_hold (application);
194 : :
195 : 3 : saw_action ("activate");
196 : 3 : }
197 : :
198 : : static void
199 : 1 : test_application_open (GApplication *application,
200 : : GFile **files,
201 : : gint n_files,
202 : : const gchar *hint)
203 : : {
204 : : GFile *f;
205 : :
206 : 1 : g_assert_cmpstr (hint, ==, "");
207 : :
208 : 1 : g_assert_cmpint (n_files, ==, 2);
209 : 1 : f = g_file_new_for_uri ("file:///a/b");
210 : 1 : g_assert (g_file_equal (files[0], f));
211 : 1 : g_object_unref (f);
212 : 1 : f = g_file_new_for_uri ("file:///c/d");
213 : 1 : g_assert (g_file_equal (files[1], f));
214 : 1 : g_object_unref (f);
215 : :
216 : 1 : saw_action ("open");
217 : 1 : }
218 : :
219 : : static void
220 : 1 : test_application_startup (GApplication *application)
221 : : {
222 : 1 : G_APPLICATION_CLASS (test_application_parent_class)
223 : 1 : ->startup (application);
224 : :
225 : 1 : g_action_map_add_action_entries (G_ACTION_MAP (application), app_actions, G_N_ELEMENTS (app_actions), application);
226 : 1 : }
227 : :
228 : : static void
229 : 17 : test_application_before_emit (GApplication *application,
230 : : GVariant *platform_data)
231 : : {
232 : : const gchar *startup_id;
233 : : gsize i;
234 : :
235 : 17 : const gchar *startup_id_keys[] = {
236 : : "desktop-startup-id",
237 : : "activation-token",
238 : : NULL,
239 : : };
240 : :
241 : 29 : for (i = 0; startup_id_keys[i] != NULL; i++)
242 : : {
243 : 23 : if (!g_variant_lookup (platform_data, startup_id_keys[i], "&s", &startup_id))
244 : 11 : return;
245 : :
246 : 12 : g_assert_cmpstr (startup_id, ==, "expected startup id");
247 : : }
248 : :
249 : 6 : saw_startup_id = TRUE;
250 : : }
251 : :
252 : : static void
253 : 1 : test_application_init (TestApplication *app)
254 : : {
255 : 1 : }
256 : :
257 : : static void
258 : 1 : test_application_class_init (GApplicationClass *class)
259 : : {
260 : 1 : class->before_emit = test_application_before_emit;
261 : 1 : class->startup = test_application_startup;
262 : 1 : class->activate = test_application_activate;
263 : 1 : class->open = test_application_open;
264 : 1 : }
265 : :
266 : : static void
267 : 1 : test_dbus_appinfo (void)
268 : : {
269 : 1 : const gchar *argv[] = { "myapp", NULL };
270 : : TestApplication *app;
271 : : int status;
272 : 1 : gchar *desktop_file = NULL;
273 : :
274 : 1 : desktop_file = g_test_build_filename (G_TEST_DIST,
275 : : "org.gtk.test.dbusappinfo.desktop",
276 : : NULL);
277 : 1 : appinfo = g_desktop_app_info_new_from_filename (desktop_file);
278 : 1 : g_assert (appinfo != NULL);
279 : 1 : g_free (desktop_file);
280 : :
281 : 1 : app = g_object_new (test_application_get_type (),
282 : : "application-id", "org.gtk.test.dbusappinfo",
283 : : "flags", G_APPLICATION_HANDLES_OPEN,
284 : : NULL);
285 : 1 : status = g_application_run (app, 1, (gchar **) argv);
286 : :
287 : 1 : g_assert_cmpint (status, ==, 0);
288 : 1 : g_assert_cmpint (current_state, ==, 8);
289 : :
290 : 1 : g_object_unref (appinfo);
291 : 1 : g_object_unref (app);
292 : 1 : }
293 : :
294 : : typedef struct {
295 : : GApplication parent;
296 : :
297 : : gboolean opened;
298 : : } TestSandboxedApplication;
299 : :
300 : : static GType test_sandboxed_application_get_type (void);
301 : : typedef GApplicationClass TestSandboxedApplicationClass;
302 : 6 : G_DEFINE_TYPE (TestSandboxedApplication, test_sandboxed_application, G_TYPE_APPLICATION)
303 : :
304 : : static void
305 : 2 : on_sandboxed_app_launch_uris_finish (GObject *object,
306 : : GAsyncResult *result,
307 : : gpointer user_data)
308 : : {
309 : 2 : GApplication *app = user_data;
310 : 2 : GError *error = NULL;
311 : :
312 : 2 : g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
313 : 2 : g_assert_no_error (error);
314 : 2 : g_assert_true (requested_startup_id);
315 : 2 : g_assert_true (saw_startup_id);
316 : :
317 : 2 : g_application_release (app);
318 : 2 : }
319 : :
320 : : static void
321 : 2 : on_sandboxed_app_activate (GApplication *app,
322 : : gpointer user_data)
323 : : {
324 : : GAppLaunchContext *ctx;
325 : 2 : GDesktopAppInfo *sandboxed_app_appinfo = user_data;
326 : : char *uri;
327 : : GList *uris;
328 : :
329 : : /* The app will be released in on_sandboxed_app_launch_uris_finish */
330 : 2 : g_application_hold (app);
331 : :
332 : 2 : uri = g_filename_to_uri (g_desktop_app_info_get_filename (sandboxed_app_appinfo), NULL, NULL);
333 : 2 : g_assert_nonnull (uri);
334 : 2 : uris = g_list_prepend (NULL, uri);
335 : 2 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
336 : 2 : requested_startup_id = FALSE;
337 : 2 : saw_startup_id = FALSE;
338 : 2 : g_app_info_launch_uris_async (G_APP_INFO (sandboxed_app_appinfo), uris, ctx,
339 : : NULL, on_sandboxed_app_launch_uris_finish, app);
340 : 2 : g_object_unref (ctx);
341 : 2 : g_list_free (uris);
342 : 2 : g_free (uri);
343 : 2 : }
344 : :
345 : : static void
346 : 2 : on_sandboxed_app_open (GApplication *app,
347 : : GFile **files,
348 : : gint n_files,
349 : : const char *hint,
350 : : gpointer user_data)
351 : : {
352 : 2 : GFakeDocumentPortalThread *portal = user_data;
353 : 2 : TestSandboxedApplication *sandboxed_app = (TestSandboxedApplication *) app;
354 : : GFile *f;
355 : : char *desktop_id;
356 : :
357 : 2 : g_assert_cmpint (n_files, ==, 1);
358 : 2 : g_test_message ("on_sandboxed_app_open received file '%s'", g_file_peek_path (files[0]));
359 : :
360 : 2 : desktop_id = g_strconcat (g_application_get_application_id (app), ".desktop", NULL);
361 : :
362 : : /* The file has been exported via the document portal */
363 : 2 : f = g_file_new_build_filename (g_fake_document_portal_thread_get_mount_point (portal),
364 : : "document-id-0",
365 : : desktop_id,
366 : : NULL);
367 : 2 : g_assert_cmpstr (g_file_peek_path (files[0]), == , g_file_peek_path (f));
368 : 2 : g_assert_true (g_file_equal (files[0], f));
369 : 2 : sandboxed_app->opened = TRUE;
370 : :
371 : 2 : g_object_unref (f);
372 : 2 : g_free (desktop_id);
373 : 2 : }
374 : :
375 : : static void
376 : 4 : test_sandboxed_application_init (TestSandboxedApplication *app)
377 : : {
378 : 4 : }
379 : :
380 : : static void
381 : 1 : test_sandboxed_application_class_init (GApplicationClass *class)
382 : : {
383 : 1 : class->before_emit = test_application_before_emit;
384 : 1 : }
385 : :
386 : : static void
387 : 2 : test_sandboxed_application_doc_export (const char *app_id,
388 : : const char *expected_portal_app_id)
389 : : {
390 : : #ifdef __GNU__
391 : : /* g_unix_fd_query_path() is not supported on Hurd, so the fake document
392 : : * portal cannot determine filenames from file descriptors. */
393 : : g_test_skip ("g_unix_fd_query_path() not supported on Hurd");
394 : : return;
395 : : #endif
396 : :
397 : 2 : const gchar *argv[] = { "myapp", NULL };
398 : 2 : gchar *desktop_file = NULL;
399 : : gchar *desktop_id;
400 : : GDesktopAppInfo *appinfo;
401 : : GApplication *app;
402 : 2 : GFakeDocumentPortalThread *thread = NULL;
403 : : int status;
404 : :
405 : : /* Run a fake-document-portal */
406 : 2 : thread = g_fake_document_portal_thread_new (session_bus_get_address (),
407 : : expected_portal_app_id);
408 : 2 : g_fake_document_portal_thread_run (thread);
409 : :
410 : 2 : desktop_id = g_strconcat (app_id, ".desktop", NULL);
411 : 2 : desktop_file = g_test_build_filename (G_TEST_DIST, desktop_id, NULL);
412 : 2 : appinfo = g_desktop_app_info_new_from_filename (desktop_file);
413 : 2 : g_assert_nonnull (appinfo);
414 : 2 : g_free (desktop_file);
415 : 2 : g_free (desktop_id);
416 : :
417 : 2 : app = g_object_new (test_sandboxed_application_get_type (),
418 : : "application-id", app_id,
419 : : "flags", G_APPLICATION_HANDLES_OPEN,
420 : : NULL);
421 : 2 : g_signal_connect (app, "activate", G_CALLBACK (on_sandboxed_app_activate),
422 : : appinfo);
423 : 2 : g_signal_connect_object (app, "open", G_CALLBACK (on_sandboxed_app_open),
424 : : thread, G_CONNECT_DEFAULT);
425 : :
426 : 2 : g_assert_false (((TestSandboxedApplication *) app)->opened);
427 : 2 : status = g_application_run (app, 1, (gchar **) argv);
428 : 2 : g_assert_cmpint (status, ==, 0);
429 : 2 : g_assert_true (((TestSandboxedApplication *) app)->opened);
430 : :
431 : 2 : g_object_unref (app);
432 : 2 : g_object_unref (appinfo);
433 : 2 : g_fake_document_portal_thread_stop (thread);
434 : 2 : g_clear_object (&thread);
435 : 2 : }
436 : :
437 : : static void
438 : 1 : test_flatpak_doc_export (void)
439 : : {
440 : 1 : g_test_summary ("Test that files opened by launching flatpak apps are made "
441 : : "available via the document portal.");
442 : :
443 : 1 : test_sandboxed_application_doc_export ("org.gtk.test.dbusappinfo.flatpak",
444 : : "org.gtk.test.dbusappinfo.flatpak");
445 : 1 : }
446 : :
447 : : static void
448 : 1 : test_snap_doc_export (void)
449 : : {
450 : 1 : g_test_summary ("Test that files opened by launching snap apps are made "
451 : : "available via the document portal.");
452 : :
453 : 1 : test_sandboxed_application_doc_export ("org.gtk.test.dbusappinfo.snap",
454 : : "snap.snap-app");
455 : 1 : }
456 : :
457 : : static void
458 : 2 : on_sandboxed_app_launch_invalid_uri_finish (GObject *object,
459 : : GAsyncResult *result,
460 : : gpointer user_data)
461 : : {
462 : 2 : GApplication *app = user_data;
463 : 2 : GError *error = NULL;
464 : :
465 : 2 : g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
466 : 2 : g_assert_no_error (error);
467 : 2 : g_assert_true (requested_startup_id);
468 : 2 : g_assert_true (saw_startup_id);
469 : :
470 : 2 : g_application_release (app);
471 : 2 : }
472 : :
473 : : static void
474 : 2 : on_sandboxed_app_activate_invalid_uri (GApplication *app,
475 : : gpointer user_data)
476 : : {
477 : : GAppLaunchContext *ctx;
478 : 2 : GDesktopAppInfo *sandboxed_app_appinfo = user_data;
479 : : GList *uris;
480 : :
481 : : /* The app will be released in on_sandboxed_app_launch_uris_finish */
482 : 2 : g_application_hold (app);
483 : :
484 : 2 : uris = g_list_prepend (NULL, "file:///hopefully/an/invalid/path.desktop");
485 : 2 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
486 : 2 : requested_startup_id = FALSE;
487 : 2 : saw_startup_id = FALSE;
488 : 2 : g_app_info_launch_uris_async (G_APP_INFO (sandboxed_app_appinfo), uris, ctx,
489 : : NULL, on_sandboxed_app_launch_invalid_uri_finish, app);
490 : 2 : g_object_unref (ctx);
491 : 2 : g_list_free (uris);
492 : 2 : }
493 : :
494 : : static void
495 : 2 : on_sandboxed_app_open_invalid_uri (GApplication *app,
496 : : GFile **files,
497 : : gint n_files,
498 : : const char *hint)
499 : : {
500 : : GFile *f;
501 : :
502 : 2 : g_assert_cmpint (n_files, ==, 1);
503 : 2 : g_test_message ("on_sandboxed_app_open received file '%s'", g_file_peek_path (files[0]));
504 : :
505 : : /* The file has been exported via the document portal */
506 : 2 : f = g_file_new_for_uri ("file:///hopefully/an/invalid/path.desktop");
507 : 2 : g_assert_cmpstr (g_file_peek_path (files[0]), == , g_file_peek_path (f));
508 : 2 : g_assert_true (g_file_equal (files[0], f));
509 : 2 : g_object_unref (f);
510 : 2 : }
511 : :
512 : : static void
513 : 2 : test_sandboxed_app_missing_doc_export (const char *app_id)
514 : : {
515 : 2 : const gchar *argv[] = { "myapp", NULL };
516 : 2 : gchar *desktop_file = NULL;
517 : : gchar *desktop_id;
518 : : GDesktopAppInfo *appinfo;
519 : : GApplication *app;
520 : : int status;
521 : 2 : GFakeDocumentPortalThread *thread = NULL;
522 : :
523 : : /* Run a fake-document-portal */
524 : 2 : thread = g_fake_document_portal_thread_new (session_bus_get_address (),
525 : : "%%_NO_PORTAL_CALLED_%%");
526 : 2 : g_fake_document_portal_thread_run (thread);
527 : :
528 : 2 : desktop_id = g_strconcat (app_id, ".desktop", NULL);
529 : 2 : desktop_file = g_test_build_filename (G_TEST_DIST, desktop_id, NULL);
530 : 2 : appinfo = g_desktop_app_info_new_from_filename (desktop_file);
531 : 2 : g_assert_nonnull (appinfo);
532 : :
533 : 2 : app = g_object_new (test_sandboxed_application_get_type (),
534 : : "application-id", app_id,
535 : : "flags", G_APPLICATION_HANDLES_OPEN,
536 : : NULL);
537 : 2 : g_signal_connect (app, "activate", G_CALLBACK (on_sandboxed_app_activate_invalid_uri),
538 : : appinfo);
539 : 2 : g_signal_connect (app, "open", G_CALLBACK (on_sandboxed_app_open_invalid_uri), NULL);
540 : :
541 : 2 : g_assert_false (((TestSandboxedApplication *) app)->opened);
542 : 2 : status = g_application_run (app, 1, (gchar **) argv);
543 : 2 : g_assert_cmpint (status, ==, 0);
544 : 2 : g_assert_false (((TestSandboxedApplication *) app)->opened);
545 : :
546 : 2 : g_object_unref (app);
547 : 2 : g_object_unref (appinfo);
548 : 2 : g_free (desktop_file);
549 : 2 : g_free (desktop_id);
550 : 2 : g_fake_document_portal_thread_stop (thread);
551 : 2 : g_clear_object (&thread);
552 : 2 : }
553 : :
554 : : static void
555 : 1 : test_flatpak_missing_doc_export (void)
556 : : {
557 : 1 : g_test_summary ("Test that files opened by launching flatpak apps are not made "
558 : : "available via the document portal.");
559 : :
560 : 1 : test_sandboxed_app_missing_doc_export ("org.gtk.test.dbusappinfo.flatpak");
561 : 1 : }
562 : :
563 : : static void
564 : 1 : test_snap_missing_doc_export (void)
565 : : {
566 : 1 : g_test_summary ("Test that files opened by launching snap apps are not made "
567 : : "available via the document portal.");
568 : :
569 : 1 : test_sandboxed_app_missing_doc_export ("org.gtk.test.dbusappinfo.snap");
570 : 1 : }
571 : :
572 : : static void
573 : 4 : check_portal_openuri_call (const char *expected_uri,
574 : : GFakeDesktopPortalThread *thread)
575 : : {
576 : 4 : const char *activation_token = NULL;
577 : 4 : GFile *expected_file = NULL;
578 : 4 : GFile *file = NULL;
579 : 4 : const char *uri = NULL;
580 : :
581 : 4 : activation_token = g_fake_desktop_portal_thread_get_last_request_activation_token (thread);
582 : 4 : uri = g_fake_desktop_portal_thread_get_last_request_uri (thread);
583 : :
584 : 4 : g_assert_cmpstr (activation_token, ==, "expected startup id");
585 : 4 : g_assert_nonnull (uri);
586 : :
587 : 4 : expected_file = g_file_new_for_uri (expected_uri);
588 : 4 : file = g_file_new_for_uri (uri);
589 : 4 : g_assert_true (g_file_equal (expected_file, file));
590 : :
591 : 4 : g_object_unref (file);
592 : 4 : g_object_unref (expected_file);
593 : 4 : }
594 : :
595 : : static void
596 : 1 : test_portal_open_file (void)
597 : : {
598 : : GAppLaunchContext *ctx;
599 : 1 : GError *error = NULL;
600 : : char *uri;
601 : 1 : GFakeDesktopPortalThread *thread = NULL;
602 : :
603 : 1 : if (!g_fake_desktop_portal_is_supported ())
604 : : {
605 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
606 : 0 : return;
607 : : }
608 : :
609 : : /* Run a fake-desktop-portal */
610 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
611 : 1 : g_fake_desktop_portal_thread_run (thread);
612 : :
613 : 1 : uri = g_filename_to_uri (g_test_get_filename (G_TEST_DIST,
614 : : "org.gtk.test.dbusappinfo.flatpak.desktop",
615 : : NULL),
616 : : NULL,
617 : : NULL);
618 : :
619 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
620 : :
621 : 1 : requested_startup_id = FALSE;
622 : :
623 : 1 : g_app_info_launch_default_for_uri (uri, ctx, &error);
624 : :
625 : 1 : g_assert_no_error (error);
626 : 1 : g_assert_true (requested_startup_id);
627 : :
628 : 1 : g_fake_desktop_portal_thread_stop (thread);
629 : 1 : check_portal_openuri_call (uri, thread);
630 : :
631 : 1 : g_clear_object (&ctx);
632 : 1 : g_clear_object (&thread);
633 : 1 : g_clear_pointer (&uri, g_free);
634 : : }
635 : :
636 : : static void
637 : 1 : test_portal_open_uri (void)
638 : : {
639 : : GAppLaunchContext *ctx;
640 : 1 : GError *error = NULL;
641 : 1 : const char *uri = "http://example.com";
642 : 1 : GFakeDesktopPortalThread *thread = NULL;
643 : :
644 : 1 : if (!g_fake_desktop_portal_is_supported ())
645 : : {
646 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
647 : 0 : return;
648 : : }
649 : :
650 : : /* Run a fake-desktop-portal */
651 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
652 : 1 : g_fake_desktop_portal_thread_run (thread);
653 : :
654 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
655 : :
656 : 1 : requested_startup_id = FALSE;
657 : :
658 : 1 : g_app_info_launch_default_for_uri (uri, ctx, &error);
659 : :
660 : 1 : g_assert_no_error (error);
661 : 1 : g_assert_true (requested_startup_id);
662 : :
663 : 1 : g_fake_desktop_portal_thread_stop (thread);
664 : 1 : check_portal_openuri_call (uri, thread);
665 : :
666 : 1 : g_clear_object (&ctx);
667 : 1 : g_clear_object (&thread);
668 : : }
669 : :
670 : : static void
671 : 2 : on_launch_default_for_uri_finished (GObject *object,
672 : : GAsyncResult *result,
673 : : gpointer user_data)
674 : : {
675 : 2 : GError *error = NULL;
676 : 2 : gboolean *called = user_data;
677 : :
678 : 2 : g_app_info_launch_default_for_uri_finish (result, &error);
679 : 2 : g_assert_no_error (error);
680 : :
681 : 2 : *called = TRUE;
682 : :
683 : 2 : g_main_context_wakeup (NULL);
684 : 2 : }
685 : :
686 : : static void
687 : 1 : test_portal_open_file_async (void)
688 : : {
689 : : GAppLaunchContext *ctx;
690 : 1 : gboolean called = FALSE;
691 : : char *uri;
692 : 1 : GFakeDesktopPortalThread *thread = NULL;
693 : :
694 : 1 : if (!g_fake_desktop_portal_is_supported ())
695 : : {
696 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
697 : 0 : return;
698 : : }
699 : :
700 : : /* Run a fake-desktop-portal */
701 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
702 : 1 : g_fake_desktop_portal_thread_run (thread);
703 : :
704 : 1 : uri = g_filename_to_uri (g_test_get_filename (G_TEST_DIST,
705 : : "org.gtk.test.dbusappinfo.flatpak.desktop",
706 : : NULL),
707 : : NULL,
708 : : NULL);
709 : :
710 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
711 : :
712 : 1 : requested_startup_id = FALSE;
713 : :
714 : 1 : g_app_info_launch_default_for_uri_async (uri, ctx,
715 : : NULL, on_launch_default_for_uri_finished, &called);
716 : :
717 : 8 : while (!called)
718 : 7 : g_main_context_iteration (NULL, TRUE);
719 : :
720 : 1 : g_assert_true (requested_startup_id);
721 : :
722 : 1 : g_fake_desktop_portal_thread_stop (thread);
723 : 1 : check_portal_openuri_call (uri, thread);
724 : :
725 : 1 : g_clear_pointer (&uri, g_free);
726 : 1 : g_clear_object (&ctx);
727 : 1 : g_clear_object (&thread);
728 : : }
729 : :
730 : : static void
731 : 1 : test_portal_open_uri_async (void)
732 : : {
733 : : GAppLaunchContext *ctx;
734 : 1 : gboolean called = FALSE;
735 : 1 : const char *uri = "http://example.com";
736 : 1 : GFakeDesktopPortalThread *thread = NULL;
737 : :
738 : 1 : if (!g_fake_desktop_portal_is_supported ())
739 : : {
740 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
741 : 0 : return;
742 : : }
743 : :
744 : : /* Run a fake-desktop-portal */
745 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
746 : 1 : g_fake_desktop_portal_thread_run (thread);
747 : :
748 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
749 : :
750 : 1 : requested_startup_id = FALSE;
751 : :
752 : 1 : g_app_info_launch_default_for_uri_async (uri, ctx,
753 : : NULL, on_launch_default_for_uri_finished, &called);
754 : :
755 : 7 : while (!called)
756 : 6 : g_main_context_iteration (NULL, TRUE);
757 : :
758 : 1 : g_assert_true (requested_startup_id);
759 : :
760 : 1 : g_fake_desktop_portal_thread_stop (thread);
761 : 1 : check_portal_openuri_call (uri, thread);
762 : :
763 : 1 : g_clear_object (&ctx);
764 : 1 : g_clear_object (&thread);
765 : : }
766 : :
767 : : int
768 : 1 : main (int argc, char **argv)
769 : : {
770 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
771 : :
772 : 1 : g_setenv ("GIO_USE_PORTALS", "1", TRUE);
773 : :
774 : 1 : g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo);
775 : 1 : g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export);
776 : 1 : g_test_add_func ("/appinfo/flatpak-missing-doc-export", test_flatpak_missing_doc_export);
777 : 1 : g_test_add_func ("/appinfo/snap-doc-export", test_snap_doc_export);
778 : 1 : g_test_add_func ("/appinfo/snap-missing-doc-export", test_snap_missing_doc_export);
779 : 1 : g_test_add_func ("/appinfo/portal-open-file", test_portal_open_file);
780 : 1 : g_test_add_func ("/appinfo/portal-open-uri", test_portal_open_uri);
781 : 1 : g_test_add_func ("/appinfo/portal-open-file-async", test_portal_open_file_async);
782 : 1 : g_test_add_func ("/appinfo/portal-open-uri-async", test_portal_open_uri_async);
783 : :
784 : 1 : return session_bus_run ();
785 : : }
|