1/*
2 * Copyright © 2013 Canonical Limited
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Ryan Lortie <desrt@desrt.ca>
18 */
19
20#include <gio/gio.h>
21#include <gio/gdesktopappinfo.h>
22
23#include "gdbus-sessionbus.h"
24
25static GDesktopAppInfo *appinfo;
26static int current_state;
27static gboolean saw_startup_id;
28static gboolean requested_startup_id;
29
30
31static GType test_app_launch_context_get_type (void);
32typedef GAppLaunchContext TestAppLaunchContext;
33typedef GAppLaunchContextClass TestAppLaunchContextClass;
34G_DEFINE_TYPE (TestAppLaunchContext, test_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
35
36static gchar *
37test_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
38 GAppInfo *info,
39 GList *uris)
40{
41 requested_startup_id = TRUE;
42 return g_strdup (str: "expected startup id");
43}
44
45static void
46test_app_launch_context_init (TestAppLaunchContext *ctx)
47{
48}
49
50static void
51test_app_launch_context_class_init (GAppLaunchContextClass *class)
52{
53 class->get_startup_notify_id = test_app_launch_context_get_startup_notify_id;
54}
55
56static GType test_application_get_type (void);
57typedef GApplication TestApplication;
58typedef GApplicationClass TestApplicationClass;
59G_DEFINE_TYPE (TestApplication, test_application, G_TYPE_APPLICATION)
60
61static void
62saw_action (const gchar *action)
63{
64 /* This is the main driver of the test. It's a bit of a state
65 * machine.
66 *
67 * Each time some event arrives on the app, it calls here to report
68 * which event it was. The initial activation of the app is what
69 * starts everything in motion (starting from state 0). At each
70 * state, we assert that we receive the expected event, send the next
71 * event, then update the current_state variable so we do the correct
72 * thing next time.
73 */
74
75 switch (current_state)
76 {
77 case 0: g_assert_cmpstr (action, ==, "activate");
78
79 /* Let's try another activation... */
80 g_app_info_launch (G_APP_INFO (appinfo), NULL, NULL, NULL);
81 current_state = 1; return; case 1: g_assert_cmpstr (action, ==, "activate");
82
83
84 /* Now let's try opening some files... */
85 {
86 GList *files;
87
88 files = g_list_prepend (NULL, data: g_file_new_for_uri (uri: "file:///a/b"));
89 files = g_list_append (list: files, data: g_file_new_for_uri (uri: "file:///c/d"));
90 g_app_info_launch (G_APP_INFO (appinfo), files, NULL, NULL);
91 g_list_free_full (list: files, free_func: g_object_unref);
92 }
93 current_state = 2; return; case 2: g_assert_cmpstr (action, ==, "open");
94
95 /* Now action activations... */
96 g_desktop_app_info_launch_action (info: appinfo, action_name: "frob", NULL);
97 current_state = 3; return; case 3: g_assert_cmpstr (action, ==, "frob");
98
99 g_desktop_app_info_launch_action (info: appinfo, action_name: "tweak", NULL);
100 current_state = 4; return; case 4: g_assert_cmpstr (action, ==, "tweak");
101
102 g_desktop_app_info_launch_action (info: appinfo, action_name: "twiddle", NULL);
103 current_state = 5; return; case 5: g_assert_cmpstr (action, ==, "twiddle");
104
105 /* Now launch the app with startup notification */
106 {
107 GAppLaunchContext *ctx;
108
109 g_assert (saw_startup_id == FALSE);
110 ctx = g_object_new (object_type: test_app_launch_context_get_type (), NULL);
111 g_app_info_launch (G_APP_INFO (appinfo), NULL, context: ctx, NULL);
112 g_assert (requested_startup_id);
113 requested_startup_id = FALSE;
114 g_object_unref (object: ctx);
115 }
116 current_state = 6; return; case 6: g_assert_cmpstr (action, ==, "activate"); g_assert (saw_startup_id);
117 saw_startup_id = FALSE;
118
119 /* Now do the same for an action */
120 {
121 GAppLaunchContext *ctx;
122
123 g_assert (saw_startup_id == FALSE);
124 ctx = g_object_new (object_type: test_app_launch_context_get_type (), NULL);
125 g_desktop_app_info_launch_action (info: appinfo, action_name: "frob", launch_context: ctx);
126 g_assert (requested_startup_id);
127 requested_startup_id = FALSE;
128 g_object_unref (object: ctx);
129 }
130 current_state = 7; return; case 7: g_assert_cmpstr (action, ==, "frob"); g_assert (saw_startup_id);
131 saw_startup_id = FALSE;
132
133 /* Now quit... */
134 g_desktop_app_info_launch_action (info: appinfo, action_name: "quit", NULL);
135 current_state = 8; return; case 8: g_assert_not_reached ();
136 }
137}
138
139static void
140test_application_frob (GSimpleAction *action,
141 GVariant *parameter,
142 gpointer user_data)
143{
144 g_assert (parameter == NULL);
145 saw_action (action: "frob");
146}
147
148static void
149test_application_tweak (GSimpleAction *action,
150 GVariant *parameter,
151 gpointer user_data)
152{
153 g_assert (parameter == NULL);
154 saw_action (action: "tweak");
155}
156
157static void
158test_application_twiddle (GSimpleAction *action,
159 GVariant *parameter,
160 gpointer user_data)
161{
162 g_assert (parameter == NULL);
163 saw_action (action: "twiddle");
164}
165
166static void
167test_application_quit (GSimpleAction *action,
168 GVariant *parameter,
169 gpointer user_data)
170{
171 GApplication *application = user_data;
172
173 g_application_quit (application);
174}
175
176static const GActionEntry app_actions[] = {
177 { "frob", test_application_frob },
178 { "tweak", test_application_tweak },
179 { "twiddle", test_application_twiddle },
180 { "quit", test_application_quit }
181};
182
183static void
184test_application_activate (GApplication *application)
185{
186 /* Unbalanced, but that's OK because we will quit() */
187 g_application_hold (application);
188
189 saw_action (action: "activate");
190}
191
192static void
193test_application_open (GApplication *application,
194 GFile **files,
195 gint n_files,
196 const gchar *hint)
197{
198 GFile *f;
199
200 g_assert_cmpstr (hint, ==, "");
201
202 g_assert_cmpint (n_files, ==, 2);
203 f = g_file_new_for_uri (uri: "file:///a/b");
204 g_assert (g_file_equal (files[0], f));
205 g_object_unref (object: f);
206 f = g_file_new_for_uri (uri: "file:///c/d");
207 g_assert (g_file_equal (files[1], f));
208 g_object_unref (object: f);
209
210 saw_action (action: "open");
211}
212
213static void
214test_application_startup (GApplication *application)
215{
216 G_APPLICATION_CLASS (test_application_parent_class)
217 ->startup (application);
218
219 g_action_map_add_action_entries (G_ACTION_MAP (application), entries: app_actions, G_N_ELEMENTS (app_actions), user_data: application);
220}
221
222static void
223test_application_before_emit (GApplication *application,
224 GVariant *platform_data)
225{
226 const gchar *startup_id;
227
228 g_assert (!saw_startup_id);
229
230 if (!g_variant_lookup (dictionary: platform_data, key: "desktop-startup-id", format_string: "&s", &startup_id))
231 return;
232
233 g_assert_cmpstr (startup_id, ==, "expected startup id");
234 saw_startup_id = TRUE;
235}
236
237static void
238test_application_init (TestApplication *app)
239{
240}
241
242static void
243test_application_class_init (GApplicationClass *class)
244{
245 class->before_emit = test_application_before_emit;
246 class->startup = test_application_startup;
247 class->activate = test_application_activate;
248 class->open = test_application_open;
249}
250
251static void
252test_dbus_appinfo (void)
253{
254 const gchar *argv[] = { "myapp", NULL };
255 TestApplication *app;
256 int status;
257 gchar *desktop_file = NULL;
258
259 desktop_file = g_test_build_filename (file_type: G_TEST_DIST,
260 first_path: "org.gtk.test.dbusappinfo.desktop",
261 NULL);
262 appinfo = g_desktop_app_info_new_from_filename (filename: desktop_file);
263 g_assert (appinfo != NULL);
264 g_free (mem: desktop_file);
265
266 app = g_object_new (object_type: test_application_get_type (),
267 first_property_name: "application-id", "org.gtk.test.dbusappinfo",
268 "flags", G_APPLICATION_HANDLES_OPEN,
269 NULL);
270 status = g_application_run (application: app, argc: 1, argv: (gchar **) argv);
271
272 g_assert_cmpint (status, ==, 0);
273 g_assert_cmpint (current_state, ==, 8);
274
275 g_object_unref (object: appinfo);
276 g_object_unref (object: app);
277}
278
279static void
280on_flatpak_launch_uris_finish (GObject *object,
281 GAsyncResult *result,
282 gpointer user_data)
283{
284 GApplication *app = user_data;
285 GError *error = NULL;
286
287 g_app_info_launch_uris_finish (G_APP_INFO (object), result, error: &error);
288 g_assert_no_error (error);
289
290 g_application_release (application: app);
291}
292
293static void
294on_flatpak_activate (GApplication *app,
295 gpointer user_data)
296{
297 GDesktopAppInfo *flatpak_appinfo = user_data;
298 char *uri;
299 GList *uris;
300
301 /* The app will be released in on_flatpak_launch_uris_finish */
302 g_application_hold (application: app);
303
304 uri = g_filename_to_uri (filename: g_desktop_app_info_get_filename (info: flatpak_appinfo), NULL, NULL);
305 g_assert_nonnull (uri);
306 uris = g_list_prepend (NULL, data: uri);
307 g_app_info_launch_uris_async (G_APP_INFO (flatpak_appinfo), uris, NULL,
308 NULL, callback: on_flatpak_launch_uris_finish, user_data: app);
309 g_list_free (list: uris);
310 g_free (mem: uri);
311}
312
313static void
314on_flatpak_open (GApplication *app,
315 GFile **files,
316 gint n_files,
317 const char *hint)
318{
319 GFile *f;
320
321 g_assert_cmpint (n_files, ==, 1);
322 g_test_message (format: "on_flatpak_open received file '%s'", g_file_peek_path (file: files[0]));
323
324 /* The file has been exported via the document portal */
325 f = g_file_new_for_uri (uri: "file:///document-portal/document-id/org.gtk.test.dbusappinfo.flatpak.desktop");
326 g_assert_true (g_file_equal (files[0], f));
327 g_object_unref (object: f);
328}
329
330static void
331test_flatpak_doc_export (void)
332{
333 const gchar *argv[] = { "myapp", NULL };
334 gchar *desktop_file = NULL;
335 GDesktopAppInfo *flatpak_appinfo;
336 GApplication *app;
337 int status;
338
339 g_test_summary (summary: "Test that files launched via Flatpak apps are made available via the document portal.");
340
341 desktop_file = g_test_build_filename (file_type: G_TEST_DIST,
342 first_path: "org.gtk.test.dbusappinfo.flatpak.desktop",
343 NULL);
344 flatpak_appinfo = g_desktop_app_info_new_from_filename (filename: desktop_file);
345 g_assert_nonnull (flatpak_appinfo);
346 g_free (mem: desktop_file);
347
348 app = g_application_new (application_id: "org.gtk.test.dbusappinfo.flatpak",
349 flags: G_APPLICATION_HANDLES_OPEN);
350 g_signal_connect (app, "activate", G_CALLBACK (on_flatpak_activate),
351 flatpak_appinfo);
352 g_signal_connect (app, "open", G_CALLBACK (on_flatpak_open), NULL);
353
354 status = g_application_run (application: app, argc: 1, argv: (gchar **) argv);
355 g_assert_cmpint (status, ==, 0);
356
357 g_object_unref (object: app);
358 g_object_unref (object: flatpak_appinfo);
359}
360
361int
362main (int argc, char **argv)
363{
364 g_test_init (argc: &argc, argv: &argv, NULL);
365
366 g_test_add_func (testpath: "/appinfo/dbusappinfo", test_func: test_dbus_appinfo);
367 g_test_add_func (testpath: "/appinfo/flatpak-doc-export", test_func: test_flatpak_doc_export);
368
369 return session_bus_run ();
370}
371

source code of gtk/subprojects/glib/gio/tests/dbus-appinfo.c