Branch data Line data Source code
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 [ + + + - : 4 : G_DEFINE_TYPE (TestAppLaunchContext, test_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
+ + ]
37 : :
38 : : static gchar *
39 : 2 : test_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
40 : : GAppInfo *info,
41 : : GList *uris)
42 : : {
43 : 2 : requested_startup_id = TRUE;
44 : 2 : return g_strdup ("expected startup id");
45 : : }
46 : :
47 : : static void
48 : 2 : test_app_launch_context_init (TestAppLaunchContext *ctx)
49 : : {
50 : 2 : }
51 : :
52 : : static void
53 : 1 : test_app_launch_context_class_init (GAppLaunchContextClass *class)
54 : : {
55 : 1 : class->get_startup_notify_id = test_app_launch_context_get_startup_notify_id;
56 : 1 : }
57 : :
58 : : static GType test_application_get_type (void);
59 : : typedef GApplication TestApplication;
60 : : typedef GApplicationClass TestApplicationClass;
61 [ + + + - : 3 : G_DEFINE_TYPE (TestApplication, test_application, G_TYPE_APPLICATION)
+ - ]
62 : :
63 : : static void
64 : 8 : 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 [ + + + + : 8 : switch (current_state)
+ + + + -
- ]
78 : : {
79 : 1 : case 0: g_assert_cmpstr (action, ==, "activate");
80 : :
81 : : /* Let's try another activation... */
82 : 1 : g_app_info_launch (G_APP_INFO (appinfo), NULL, NULL, NULL);
83 : 2 : 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 : 1 : files = g_list_prepend (NULL, g_file_new_for_uri ("file:///a/b"));
91 : 1 : files = g_list_append (files, g_file_new_for_uri ("file:///c/d"));
92 : 1 : g_app_info_launch (G_APP_INFO (appinfo), files, NULL, NULL);
93 : 1 : g_list_free_full (files, g_object_unref);
94 : : }
95 : 2 : current_state = 2; return; case 2: g_assert_cmpstr (action, ==, "open");
96 : :
97 : : /* Now action activations... */
98 : 1 : g_desktop_app_info_launch_action (appinfo, "frob", NULL);
99 : 2 : current_state = 3; return; case 3: g_assert_cmpstr (action, ==, "frob");
100 : :
101 : 1 : g_desktop_app_info_launch_action (appinfo, "tweak", NULL);
102 : 2 : current_state = 4; return; case 4: g_assert_cmpstr (action, ==, "tweak");
103 : :
104 : 1 : g_desktop_app_info_launch_action (appinfo, "twiddle", NULL);
105 : 2 : 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 : 1 : g_assert (saw_startup_id == FALSE);
112 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
113 : 1 : g_app_info_launch (G_APP_INFO (appinfo), NULL, ctx, NULL);
114 : 1 : g_assert (requested_startup_id);
115 : 1 : requested_startup_id = FALSE;
116 : 1 : g_object_unref (ctx);
117 : : }
118 : 2 : current_state = 6; return; case 6: g_assert_cmpstr (action, ==, "activate"); g_assert (saw_startup_id);
119 : 1 : saw_startup_id = FALSE;
120 : :
121 : : /* Now do the same for an action */
122 : : {
123 : : GAppLaunchContext *ctx;
124 : :
125 : 1 : g_assert (saw_startup_id == FALSE);
126 : 1 : ctx = g_object_new (test_app_launch_context_get_type (), NULL);
127 : 1 : g_desktop_app_info_launch_action (appinfo, "frob", ctx);
128 : 1 : g_assert (requested_startup_id);
129 : 1 : requested_startup_id = FALSE;
130 : 1 : g_object_unref (ctx);
131 : : }
132 : 2 : current_state = 7; return; case 7: g_assert_cmpstr (action, ==, "frob"); g_assert (saw_startup_id);
133 : 1 : saw_startup_id = FALSE;
134 : :
135 : : /* Now quit... */
136 : 1 : 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 : 2 : test_application_frob (GSimpleAction *action,
143 : : GVariant *parameter,
144 : : gpointer user_data)
145 : : {
146 : 2 : g_assert (parameter == NULL);
147 : 2 : saw_action ("frob");
148 : 2 : }
149 : :
150 : : static void
151 : 1 : test_application_tweak (GSimpleAction *action,
152 : : GVariant *parameter,
153 : : gpointer user_data)
154 : : {
155 : 1 : g_assert (parameter == NULL);
156 : 1 : saw_action ("tweak");
157 : 1 : }
158 : :
159 : : static void
160 : 1 : test_application_twiddle (GSimpleAction *action,
161 : : GVariant *parameter,
162 : : gpointer user_data)
163 : : {
164 : 1 : g_assert (parameter == NULL);
165 : 1 : saw_action ("twiddle");
166 : 1 : }
167 : :
168 : : static void
169 : 1 : test_application_quit (GSimpleAction *action,
170 : : GVariant *parameter,
171 : : gpointer user_data)
172 : : {
173 : 1 : GApplication *application = user_data;
174 : :
175 : 1 : g_application_quit (application);
176 : 1 : }
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 : 3 : test_application_activate (GApplication *application)
187 : : {
188 : : /* Unbalanced, but that's OK because we will quit() */
189 : 3 : g_application_hold (application);
190 : :
191 : 3 : saw_action ("activate");
192 : 3 : }
193 : :
194 : : static void
195 : 1 : test_application_open (GApplication *application,
196 : : GFile **files,
197 : : gint n_files,
198 : : const gchar *hint)
199 : : {
200 : : GFile *f;
201 : :
202 : 1 : g_assert_cmpstr (hint, ==, "");
203 : :
204 : 1 : g_assert_cmpint (n_files, ==, 2);
205 : 1 : f = g_file_new_for_uri ("file:///a/b");
206 : 1 : g_assert (g_file_equal (files[0], f));
207 : 1 : g_object_unref (f);
208 : 1 : f = g_file_new_for_uri ("file:///c/d");
209 : 1 : g_assert (g_file_equal (files[1], f));
210 : 1 : g_object_unref (f);
211 : :
212 : 1 : saw_action ("open");
213 : 1 : }
214 : :
215 : : static void
216 : 1 : test_application_startup (GApplication *application)
217 : : {
218 : 1 : G_APPLICATION_CLASS (test_application_parent_class)
219 : 1 : ->startup (application);
220 : :
221 : 1 : g_action_map_add_action_entries (G_ACTION_MAP (application), app_actions, G_N_ELEMENTS (app_actions), application);
222 : 1 : }
223 : :
224 : : static void
225 : 8 : test_application_before_emit (GApplication *application,
226 : : GVariant *platform_data)
227 : : {
228 : : const gchar *startup_id;
229 : : gsize i;
230 : :
231 : 8 : g_assert (!saw_startup_id);
232 : :
233 : 8 : const gchar *startup_id_keys[] = {
234 : : "desktop-startup-id",
235 : : "activation-token",
236 : : NULL,
237 : : };
238 : :
239 [ + + ]: 12 : for (i = 0; startup_id_keys[i] != NULL; i++)
240 : : {
241 [ + + ]: 10 : if (!g_variant_lookup (platform_data, startup_id_keys[i], "&s", &startup_id))
242 : 6 : return;
243 : :
244 : 4 : g_assert_cmpstr (startup_id, ==, "expected startup id");
245 : : }
246 : :
247 : 2 : saw_startup_id = TRUE;
248 : : }
249 : :
250 : : static void
251 : 1 : test_application_init (TestApplication *app)
252 : : {
253 : 1 : }
254 : :
255 : : static void
256 : 1 : test_application_class_init (GApplicationClass *class)
257 : : {
258 : 1 : class->before_emit = test_application_before_emit;
259 : 1 : class->startup = test_application_startup;
260 : 1 : class->activate = test_application_activate;
261 : 1 : class->open = test_application_open;
262 : 1 : }
263 : :
264 : : static void
265 : 1 : test_dbus_appinfo (void)
266 : : {
267 : 1 : const gchar *argv[] = { "myapp", NULL };
268 : : TestApplication *app;
269 : : int status;
270 : 1 : gchar *desktop_file = NULL;
271 : :
272 : 1 : desktop_file = g_test_build_filename (G_TEST_DIST,
273 : : "org.gtk.test.dbusappinfo.desktop",
274 : : NULL);
275 : 1 : appinfo = g_desktop_app_info_new_from_filename (desktop_file);
276 : 1 : g_assert (appinfo != NULL);
277 : 1 : g_free (desktop_file);
278 : :
279 : 1 : app = g_object_new (test_application_get_type (),
280 : : "application-id", "org.gtk.test.dbusappinfo",
281 : : "flags", G_APPLICATION_HANDLES_OPEN,
282 : : NULL);
283 : 1 : status = g_application_run (app, 1, (gchar **) argv);
284 : :
285 : 1 : g_assert_cmpint (status, ==, 0);
286 : 1 : g_assert_cmpint (current_state, ==, 8);
287 : :
288 : 1 : g_object_unref (appinfo);
289 : 1 : g_object_unref (app);
290 : 1 : }
291 : :
292 : : static void
293 : 1 : on_flatpak_launch_uris_finish (GObject *object,
294 : : GAsyncResult *result,
295 : : gpointer user_data)
296 : : {
297 : 1 : GApplication *app = user_data;
298 : 1 : GError *error = NULL;
299 : :
300 : 1 : g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
301 : 1 : g_assert_no_error (error);
302 : :
303 : 1 : g_application_release (app);
304 : 1 : }
305 : :
306 : : static void
307 : 1 : on_flatpak_activate (GApplication *app,
308 : : gpointer user_data)
309 : : {
310 : 1 : 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 : 1 : g_application_hold (app);
316 : :
317 : 1 : uri = g_filename_to_uri (g_desktop_app_info_get_filename (flatpak_appinfo), NULL, NULL);
318 : 1 : g_assert_nonnull (uri);
319 : 1 : uris = g_list_prepend (NULL, uri);
320 : 1 : g_app_info_launch_uris_async (G_APP_INFO (flatpak_appinfo), uris, NULL,
321 : : NULL, on_flatpak_launch_uris_finish, app);
322 : 1 : g_list_free (uris);
323 : 1 : g_free (uri);
324 : 1 : }
325 : :
326 : : static void
327 : 1 : on_flatpak_open (GApplication *app,
328 : : GFile **files,
329 : : gint n_files,
330 : : const char *hint)
331 : : {
332 : : GFile *f;
333 : :
334 : 1 : g_assert_cmpint (n_files, ==, 1);
335 : 1 : 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 : 1 : f = g_file_new_for_uri ("file:///document-portal/document-id/org.gtk.test.dbusappinfo.flatpak.desktop");
339 : 1 : g_assert_true (g_file_equal (files[0], f));
340 : 1 : g_object_unref (f);
341 : 1 : }
342 : :
343 : : static void
344 : 1 : test_flatpak_doc_export (void)
345 : : {
346 : 1 : const gchar *argv[] = { "myapp", NULL };
347 : 1 : gchar *desktop_file = NULL;
348 : : GDesktopAppInfo *flatpak_appinfo;
349 : : GApplication *app;
350 : : int status;
351 : :
352 : 1 : g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal.");
353 : :
354 : 1 : desktop_file = g_test_build_filename (G_TEST_DIST,
355 : : "org.gtk.test.dbusappinfo.flatpak.desktop",
356 : : NULL);
357 : 1 : flatpak_appinfo = g_desktop_app_info_new_from_filename (desktop_file);
358 : 1 : g_assert_nonnull (flatpak_appinfo);
359 : 1 : g_free (desktop_file);
360 : :
361 : 1 : app = g_application_new ("org.gtk.test.dbusappinfo.flatpak",
362 : : G_APPLICATION_HANDLES_OPEN);
363 : 1 : g_signal_connect (app, "activate", G_CALLBACK (on_flatpak_activate),
364 : : flatpak_appinfo);
365 : 1 : g_signal_connect (app, "open", G_CALLBACK (on_flatpak_open), NULL);
366 : :
367 : 1 : status = g_application_run (app, 1, (gchar **) argv);
368 : 1 : g_assert_cmpint (status, ==, 0);
369 : :
370 : 1 : g_object_unref (app);
371 : 1 : g_object_unref (flatpak_appinfo);
372 : 1 : }
373 : :
374 : : static void
375 : 1 : on_flatpak_launch_invalid_uri_finish (GObject *object,
376 : : GAsyncResult *result,
377 : : gpointer user_data)
378 : : {
379 : 1 : GApplication *app = user_data;
380 : 1 : GError *error = NULL;
381 : :
382 : 1 : g_app_info_launch_uris_finish (G_APP_INFO (object), result, &error);
383 : 1 : g_assert_no_error (error);
384 : :
385 : 1 : g_application_release (app);
386 : 1 : }
387 : :
388 : : static void
389 : 1 : on_flatpak_activate_invalid_uri (GApplication *app,
390 : : gpointer user_data)
391 : : {
392 : 1 : GDesktopAppInfo *flatpak_appinfo = user_data;
393 : : GList *uris;
394 : :
395 : : /* The app will be released in on_flatpak_launch_uris_finish */
396 : 1 : g_application_hold (app);
397 : :
398 : 1 : uris = g_list_prepend (NULL, "file:///hopefully/an/invalid/path.desktop");
399 : 1 : g_app_info_launch_uris_async (G_APP_INFO (flatpak_appinfo), uris, NULL,
400 : : NULL, on_flatpak_launch_invalid_uri_finish, app);
401 : 1 : g_list_free (uris);
402 : 1 : }
403 : :
404 : : static void
405 : 1 : on_flatpak_open_invalid_uri (GApplication *app,
406 : : GFile **files,
407 : : gint n_files,
408 : : const char *hint)
409 : : {
410 : : GFile *f;
411 : :
412 : 1 : g_assert_cmpint (n_files, ==, 1);
413 : 1 : 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 : 1 : f = g_file_new_for_uri ("file:///hopefully/an/invalid/path.desktop");
417 : 1 : g_assert_true (g_file_equal (files[0], f));
418 : 1 : g_object_unref (f);
419 : 1 : }
420 : :
421 : : static void
422 : 1 : test_flatpak_missing_doc_export (void)
423 : : {
424 : 1 : const gchar *argv[] = { "myapp", NULL };
425 : 1 : gchar *desktop_file = NULL;
426 : : GDesktopAppInfo *flatpak_appinfo;
427 : : GApplication *app;
428 : : int status;
429 : :
430 : 1 : g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal.");
431 : :
432 : 1 : desktop_file = g_test_build_filename (G_TEST_DIST,
433 : : "org.gtk.test.dbusappinfo.flatpak.desktop",
434 : : NULL);
435 : 1 : flatpak_appinfo = g_desktop_app_info_new_from_filename (desktop_file);
436 : 1 : g_assert_nonnull (flatpak_appinfo);
437 : :
438 : 1 : app = g_application_new ("org.gtk.test.dbusappinfo.flatpak",
439 : : G_APPLICATION_HANDLES_OPEN);
440 : 1 : g_signal_connect (app, "activate", G_CALLBACK (on_flatpak_activate_invalid_uri),
441 : : flatpak_appinfo);
442 : 1 : g_signal_connect (app, "open", G_CALLBACK (on_flatpak_open_invalid_uri), NULL);
443 : :
444 : 1 : status = g_application_run (app, 1, (gchar **) argv);
445 : 1 : g_assert_cmpint (status, ==, 0);
446 : :
447 : 1 : g_object_unref (app);
448 : 1 : g_object_unref (flatpak_appinfo);
449 : 1 : g_free (desktop_file);
450 : 1 : }
451 : :
452 : : int
453 : 1 : main (int argc, char **argv)
454 : : {
455 : 1 : g_test_init (&argc, &argv, NULL);
456 : :
457 : 1 : g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo);
458 : 1 : g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export);
459 : 1 : g_test_add_func ("/appinfo/flatpak-missing-doc-export", test_flatpak_missing_doc_export);
460 : :
461 : 1 : return session_bus_run ();
462 : : }
|