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 : 2 : const gchar *argv[] = { "myapp", NULL };
391 : 2 : gchar *desktop_file = NULL;
392 : : gchar *desktop_id;
393 : : GDesktopAppInfo *appinfo;
394 : : GApplication *app;
395 : 2 : GFakeDocumentPortalThread *thread = NULL;
396 : : int status;
397 : :
398 : : /* Run a fake-document-portal */
399 : 2 : thread = g_fake_document_portal_thread_new (session_bus_get_address (),
400 : : expected_portal_app_id);
401 : 2 : g_fake_document_portal_thread_run (thread);
402 : :
403 : 2 : desktop_id = g_strconcat (app_id, ".desktop", NULL);
404 : 2 : desktop_file = g_test_build_filename (G_TEST_DIST, desktop_id, NULL);
405 : 2 : appinfo = g_desktop_app_info_new_from_filename (desktop_file);
406 : 2 : g_assert_nonnull (appinfo);
407 : 2 : g_free (desktop_file);
408 : 2 : g_free (desktop_id);
409 : :
410 : 2 : app = g_object_new (test_sandboxed_application_get_type (),
411 : : "application-id", app_id,
412 : : "flags", G_APPLICATION_HANDLES_OPEN,
413 : : NULL);
414 : 2 : g_signal_connect (app, "activate", G_CALLBACK (on_sandboxed_app_activate),
415 : : appinfo);
416 : 2 : g_signal_connect_object (app, "open", G_CALLBACK (on_sandboxed_app_open),
417 : : thread, G_CONNECT_DEFAULT);
418 : :
419 : 2 : g_assert_false (((TestSandboxedApplication *) app)->opened);
420 : 2 : status = g_application_run (app, 1, (gchar **) argv);
421 : 2 : g_assert_cmpint (status, ==, 0);
422 : 2 : g_assert_true (((TestSandboxedApplication *) app)->opened);
423 : :
424 : 2 : g_object_unref (app);
425 : 2 : g_object_unref (appinfo);
426 : 2 : g_fake_document_portal_thread_stop (thread);
427 : 2 : g_clear_object (&thread);
428 : 2 : }
429 : :
430 : : static void
431 : 1 : test_flatpak_doc_export (void)
432 : : {
433 : 1 : g_test_summary ("Test that files opened by launching flatpak apps are made "
434 : : "available via the document portal.");
435 : :
436 : 1 : test_sandboxed_application_doc_export ("org.gtk.test.dbusappinfo.flatpak",
437 : : "org.gtk.test.dbusappinfo.flatpak");
438 : 1 : }
439 : :
440 : : static void
441 : 1 : test_snap_doc_export (void)
442 : : {
443 : 1 : g_test_summary ("Test that files opened by launching snap apps are made "
444 : : "available via the document portal.");
445 : :
446 : 1 : test_sandboxed_application_doc_export ("org.gtk.test.dbusappinfo.snap",
447 : : "snap.snap-app");
448 : 1 : }
449 : :
450 : : static void
451 : 2 : on_sandboxed_app_launch_invalid_uri_finish (GObject *object,
452 : : GAsyncResult *result,
453 : : gpointer user_data)
454 : : {
455 : 2 : GApplication *app = user_data;
456 : 2 : GError *error = NULL;
457 : :
458 : 2 : g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
459 : 2 : g_assert_no_error (error);
460 : 2 : g_assert_true (requested_startup_id);
461 : 2 : g_assert_true (saw_startup_id);
462 : :
463 : 2 : g_application_release (app);
464 : 2 : }
465 : :
466 : : static void
467 : 2 : on_sandboxed_app_activate_invalid_uri (GApplication *app,
468 : : gpointer user_data)
469 : : {
470 : : GAppLaunchContext *ctx;
471 : 2 : GDesktopAppInfo *sandboxed_app_appinfo = user_data;
472 : : GList *uris;
473 : :
474 : : /* The app will be released in on_sandboxed_app_launch_uris_finish */
475 : 2 : g_application_hold (app);
476 : :
477 : 2 : uris = g_list_prepend (NULL, "file:///hopefully/an/invalid/path.desktop");
478 : 2 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
479 : 2 : requested_startup_id = FALSE;
480 : 2 : saw_startup_id = FALSE;
481 : 2 : g_app_info_launch_uris_async (G_APP_INFO (sandboxed_app_appinfo), uris, ctx,
482 : : NULL, on_sandboxed_app_launch_invalid_uri_finish, app);
483 : 2 : g_object_unref (ctx);
484 : 2 : g_list_free (uris);
485 : 2 : }
486 : :
487 : : static void
488 : 2 : on_sandboxed_app_open_invalid_uri (GApplication *app,
489 : : GFile **files,
490 : : gint n_files,
491 : : const char *hint)
492 : : {
493 : : GFile *f;
494 : :
495 : 2 : g_assert_cmpint (n_files, ==, 1);
496 : 2 : g_test_message ("on_sandboxed_app_open received file '%s'", g_file_peek_path (files[0]));
497 : :
498 : : /* The file has been exported via the document portal */
499 : 2 : f = g_file_new_for_uri ("file:///hopefully/an/invalid/path.desktop");
500 : 2 : g_assert_cmpstr (g_file_peek_path (files[0]), == , g_file_peek_path (f));
501 : 2 : g_assert_true (g_file_equal (files[0], f));
502 : 2 : g_object_unref (f);
503 : 2 : }
504 : :
505 : : static void
506 : 2 : test_sandboxed_app_missing_doc_export (const char *app_id)
507 : : {
508 : 2 : const gchar *argv[] = { "myapp", NULL };
509 : 2 : gchar *desktop_file = NULL;
510 : : gchar *desktop_id;
511 : : GDesktopAppInfo *appinfo;
512 : : GApplication *app;
513 : : int status;
514 : 2 : GFakeDocumentPortalThread *thread = NULL;
515 : :
516 : : /* Run a fake-document-portal */
517 : 2 : thread = g_fake_document_portal_thread_new (session_bus_get_address (),
518 : : "%%_NO_PORTAL_CALLED_%%");
519 : 2 : g_fake_document_portal_thread_run (thread);
520 : :
521 : 2 : desktop_id = g_strconcat (app_id, ".desktop", NULL);
522 : 2 : desktop_file = g_test_build_filename (G_TEST_DIST, desktop_id, NULL);
523 : 2 : appinfo = g_desktop_app_info_new_from_filename (desktop_file);
524 : 2 : g_assert_nonnull (appinfo);
525 : :
526 : 2 : app = g_object_new (test_sandboxed_application_get_type (),
527 : : "application-id", app_id,
528 : : "flags", G_APPLICATION_HANDLES_OPEN,
529 : : NULL);
530 : 2 : g_signal_connect (app, "activate", G_CALLBACK (on_sandboxed_app_activate_invalid_uri),
531 : : appinfo);
532 : 2 : g_signal_connect (app, "open", G_CALLBACK (on_sandboxed_app_open_invalid_uri), NULL);
533 : :
534 : 2 : g_assert_false (((TestSandboxedApplication *) app)->opened);
535 : 2 : status = g_application_run (app, 1, (gchar **) argv);
536 : 2 : g_assert_cmpint (status, ==, 0);
537 : 2 : g_assert_false (((TestSandboxedApplication *) app)->opened);
538 : :
539 : 2 : g_object_unref (app);
540 : 2 : g_object_unref (appinfo);
541 : 2 : g_free (desktop_file);
542 : 2 : g_free (desktop_id);
543 : 2 : g_fake_document_portal_thread_stop (thread);
544 : 2 : g_clear_object (&thread);
545 : 2 : }
546 : :
547 : : static void
548 : 1 : test_flatpak_missing_doc_export (void)
549 : : {
550 : 1 : g_test_summary ("Test that files opened by launching flatpak apps are not made "
551 : : "available via the document portal.");
552 : :
553 : 1 : test_sandboxed_app_missing_doc_export ("org.gtk.test.dbusappinfo.flatpak");
554 : 1 : }
555 : :
556 : : static void
557 : 1 : test_snap_missing_doc_export (void)
558 : : {
559 : 1 : g_test_summary ("Test that files opened by launching snap apps are not made "
560 : : "available via the document portal.");
561 : :
562 : 1 : test_sandboxed_app_missing_doc_export ("org.gtk.test.dbusappinfo.snap");
563 : 1 : }
564 : :
565 : : static void
566 : 4 : check_portal_openuri_call (const char *expected_uri,
567 : : GFakeDesktopPortalThread *thread)
568 : : {
569 : 4 : const char *activation_token = NULL;
570 : 4 : GFile *expected_file = NULL;
571 : 4 : GFile *file = NULL;
572 : 4 : const char *uri = NULL;
573 : :
574 : 4 : activation_token = g_fake_desktop_portal_thread_get_last_request_activation_token (thread);
575 : 4 : uri = g_fake_desktop_portal_thread_get_last_request_uri (thread);
576 : :
577 : 4 : g_assert_cmpstr (activation_token, ==, "expected startup id");
578 : 4 : g_assert_nonnull (uri);
579 : :
580 : 4 : expected_file = g_file_new_for_uri (expected_uri);
581 : 4 : file = g_file_new_for_uri (uri);
582 : 4 : g_assert_true (g_file_equal (expected_file, file));
583 : :
584 : 4 : g_object_unref (file);
585 : 4 : g_object_unref (expected_file);
586 : 4 : }
587 : :
588 : : static void
589 : 1 : test_portal_open_file (void)
590 : : {
591 : : GAppLaunchContext *ctx;
592 : 1 : GError *error = NULL;
593 : : char *uri;
594 : 1 : GFakeDesktopPortalThread *thread = NULL;
595 : :
596 : 1 : if (!g_fake_desktop_portal_is_supported ())
597 : : {
598 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
599 : 0 : return;
600 : : }
601 : :
602 : : /* Run a fake-desktop-portal */
603 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
604 : 1 : g_fake_desktop_portal_thread_run (thread);
605 : :
606 : 1 : uri = g_filename_to_uri (g_test_get_filename (G_TEST_DIST,
607 : : "org.gtk.test.dbusappinfo.flatpak.desktop",
608 : : NULL),
609 : : NULL,
610 : : NULL);
611 : :
612 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
613 : :
614 : 1 : requested_startup_id = FALSE;
615 : :
616 : 1 : g_app_info_launch_default_for_uri (uri, ctx, &error);
617 : :
618 : 1 : g_assert_no_error (error);
619 : 1 : g_assert_true (requested_startup_id);
620 : :
621 : 1 : g_fake_desktop_portal_thread_stop (thread);
622 : 1 : check_portal_openuri_call (uri, thread);
623 : :
624 : 1 : g_clear_object (&ctx);
625 : 1 : g_clear_object (&thread);
626 : 1 : g_clear_pointer (&uri, g_free);
627 : : }
628 : :
629 : : static void
630 : 1 : test_portal_open_uri (void)
631 : : {
632 : : GAppLaunchContext *ctx;
633 : 1 : GError *error = NULL;
634 : 1 : const char *uri = "http://example.com";
635 : 1 : GFakeDesktopPortalThread *thread = NULL;
636 : :
637 : 1 : if (!g_fake_desktop_portal_is_supported ())
638 : : {
639 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
640 : 0 : return;
641 : : }
642 : :
643 : : /* Run a fake-desktop-portal */
644 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
645 : 1 : g_fake_desktop_portal_thread_run (thread);
646 : :
647 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
648 : :
649 : 1 : requested_startup_id = FALSE;
650 : :
651 : 1 : g_app_info_launch_default_for_uri (uri, ctx, &error);
652 : :
653 : 1 : g_assert_no_error (error);
654 : 1 : g_assert_true (requested_startup_id);
655 : :
656 : 1 : g_fake_desktop_portal_thread_stop (thread);
657 : 1 : check_portal_openuri_call (uri, thread);
658 : :
659 : 1 : g_clear_object (&ctx);
660 : 1 : g_clear_object (&thread);
661 : : }
662 : :
663 : : static void
664 : 2 : on_launch_default_for_uri_finished (GObject *object,
665 : : GAsyncResult *result,
666 : : gpointer user_data)
667 : : {
668 : 2 : GError *error = NULL;
669 : 2 : gboolean *called = user_data;
670 : :
671 : 2 : g_app_info_launch_default_for_uri_finish (result, &error);
672 : 2 : g_assert_no_error (error);
673 : :
674 : 2 : *called = TRUE;
675 : :
676 : 2 : g_main_context_wakeup (NULL);
677 : 2 : }
678 : :
679 : : static void
680 : 1 : test_portal_open_file_async (void)
681 : : {
682 : : GAppLaunchContext *ctx;
683 : 1 : gboolean called = FALSE;
684 : : char *uri;
685 : 1 : GFakeDesktopPortalThread *thread = NULL;
686 : :
687 : 1 : if (!g_fake_desktop_portal_is_supported ())
688 : : {
689 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
690 : 0 : return;
691 : : }
692 : :
693 : : /* Run a fake-desktop-portal */
694 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
695 : 1 : g_fake_desktop_portal_thread_run (thread);
696 : :
697 : 1 : uri = g_filename_to_uri (g_test_get_filename (G_TEST_DIST,
698 : : "org.gtk.test.dbusappinfo.flatpak.desktop",
699 : : NULL),
700 : : NULL,
701 : : NULL);
702 : :
703 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
704 : :
705 : 1 : requested_startup_id = FALSE;
706 : :
707 : 1 : g_app_info_launch_default_for_uri_async (uri, ctx,
708 : : NULL, on_launch_default_for_uri_finished, &called);
709 : :
710 : 8 : while (!called)
711 : 7 : g_main_context_iteration (NULL, TRUE);
712 : :
713 : 1 : g_assert_true (requested_startup_id);
714 : :
715 : 1 : g_fake_desktop_portal_thread_stop (thread);
716 : 1 : check_portal_openuri_call (uri, thread);
717 : :
718 : 1 : g_clear_pointer (&uri, g_free);
719 : 1 : g_clear_object (&ctx);
720 : 1 : g_clear_object (&thread);
721 : : }
722 : :
723 : : static void
724 : 1 : test_portal_open_uri_async (void)
725 : : {
726 : : GAppLaunchContext *ctx;
727 : 1 : gboolean called = FALSE;
728 : 1 : const char *uri = "http://example.com";
729 : 1 : GFakeDesktopPortalThread *thread = NULL;
730 : :
731 : 1 : if (!g_fake_desktop_portal_is_supported ())
732 : : {
733 : 0 : g_test_skip ("fake-desktop-portal not currently supported on this platform");
734 : 0 : return;
735 : : }
736 : :
737 : : /* Run a fake-desktop-portal */
738 : 1 : thread = g_fake_desktop_portal_thread_new (session_bus_get_address ());
739 : 1 : g_fake_desktop_portal_thread_run (thread);
740 : :
741 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
742 : :
743 : 1 : requested_startup_id = FALSE;
744 : :
745 : 1 : g_app_info_launch_default_for_uri_async (uri, ctx,
746 : : NULL, on_launch_default_for_uri_finished, &called);
747 : :
748 : 7 : while (!called)
749 : 6 : g_main_context_iteration (NULL, TRUE);
750 : :
751 : 1 : g_assert_true (requested_startup_id);
752 : :
753 : 1 : g_fake_desktop_portal_thread_stop (thread);
754 : 1 : check_portal_openuri_call (uri, thread);
755 : :
756 : 1 : g_clear_object (&ctx);
757 : 1 : g_clear_object (&thread);
758 : : }
759 : :
760 : : int
761 : 1 : main (int argc, char **argv)
762 : : {
763 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
764 : :
765 : 1 : g_setenv ("GIO_USE_PORTALS", "1", TRUE);
766 : :
767 : 1 : g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo);
768 : 1 : g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export);
769 : 1 : g_test_add_func ("/appinfo/flatpak-missing-doc-export", test_flatpak_missing_doc_export);
770 : 1 : g_test_add_func ("/appinfo/snap-doc-export", test_snap_doc_export);
771 : 1 : g_test_add_func ("/appinfo/snap-missing-doc-export", test_snap_missing_doc_export);
772 : 1 : g_test_add_func ("/appinfo/portal-open-file", test_portal_open_file);
773 : 1 : g_test_add_func ("/appinfo/portal-open-uri", test_portal_open_uri);
774 : 1 : g_test_add_func ("/appinfo/portal-open-file-async", test_portal_open_file_async);
775 : 1 : g_test_add_func ("/appinfo/portal-open-uri-async", test_portal_open_uri_async);
776 : :
777 : 1 : return session_bus_run ();
778 : : }
|