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
25 static GDesktopAppInfo *appinfo;
26 static int current_state;
27 static gboolean saw_startup_id;
28 static gboolean requested_startup_id;
29
30
31 static GType test_app_launch_context_get_type (void);
32 typedef GAppLaunchContext TestAppLaunchContext;
33 typedef GAppLaunchContextClass TestAppLaunchContextClass;
G_DEFINE_TYPE(TestAppLaunchContext,test_app_launch_context,G_TYPE_APP_LAUNCH_CONTEXT)34 G_DEFINE_TYPE (TestAppLaunchContext, test_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
35
36 static gchar *
37 test_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 ("expected startup id");
43 }
44
45 static void
test_app_launch_context_init(TestAppLaunchContext * ctx)46 test_app_launch_context_init (TestAppLaunchContext *ctx)
47 {
48 }
49
50 static void
test_app_launch_context_class_init(GAppLaunchContextClass * class)51 test_app_launch_context_class_init (GAppLaunchContextClass *class)
52 {
53 class->get_startup_notify_id = test_app_launch_context_get_startup_notify_id;
54 }
55
56 static GType test_application_get_type (void);
57 typedef GApplication TestApplication;
58 typedef GApplicationClass TestApplicationClass;
G_DEFINE_TYPE(TestApplication,test_application,G_TYPE_APPLICATION)59 G_DEFINE_TYPE (TestApplication, test_application, G_TYPE_APPLICATION)
60
61 static void
62 saw_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, g_file_new_for_uri ("file:///a/b"));
89 files = g_list_append (files, g_file_new_for_uri ("file:///c/d"));
90 g_app_info_launch (G_APP_INFO (appinfo), files, NULL, NULL);
91 g_list_free_full (files, 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 (appinfo, "frob", NULL);
97 current_state = 3; return; case 3: g_assert_cmpstr (action, ==, "frob");
98
99 g_desktop_app_info_launch_action (appinfo, "tweak", NULL);
100 current_state = 4; return; case 4: g_assert_cmpstr (action, ==, "tweak");
101
102 g_desktop_app_info_launch_action (appinfo, "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 (test_app_launch_context_get_type (), NULL);
111 g_app_info_launch (G_APP_INFO (appinfo), NULL, ctx, NULL);
112 g_assert (requested_startup_id);
113 requested_startup_id = FALSE;
114 g_object_unref (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 (test_app_launch_context_get_type (), NULL);
125 g_desktop_app_info_launch_action (appinfo, "frob", ctx);
126 g_assert (requested_startup_id);
127 requested_startup_id = FALSE;
128 g_object_unref (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 (appinfo, "quit", NULL);
135 current_state = 8; return; case 8: g_assert_not_reached ();
136 }
137 }
138
139 static void
test_application_frob(GSimpleAction * action,GVariant * parameter,gpointer user_data)140 test_application_frob (GSimpleAction *action,
141 GVariant *parameter,
142 gpointer user_data)
143 {
144 g_assert (parameter == NULL);
145 saw_action ("frob");
146 }
147
148 static void
test_application_tweak(GSimpleAction * action,GVariant * parameter,gpointer user_data)149 test_application_tweak (GSimpleAction *action,
150 GVariant *parameter,
151 gpointer user_data)
152 {
153 g_assert (parameter == NULL);
154 saw_action ("tweak");
155 }
156
157 static void
test_application_twiddle(GSimpleAction * action,GVariant * parameter,gpointer user_data)158 test_application_twiddle (GSimpleAction *action,
159 GVariant *parameter,
160 gpointer user_data)
161 {
162 g_assert (parameter == NULL);
163 saw_action ("twiddle");
164 }
165
166 static void
test_application_quit(GSimpleAction * action,GVariant * parameter,gpointer user_data)167 test_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
176 static const GActionEntry app_actions[] = {
177 { "frob", test_application_frob, NULL, NULL, NULL, { 0 } },
178 { "tweak", test_application_tweak, NULL, NULL, NULL, { 0 } },
179 { "twiddle", test_application_twiddle, NULL, NULL, NULL, { 0 } },
180 { "quit", test_application_quit, NULL, NULL, NULL, { 0 } }
181 };
182
183 static void
test_application_activate(GApplication * application)184 test_application_activate (GApplication *application)
185 {
186 /* Unbalanced, but that's OK because we will quit() */
187 g_application_hold (application);
188
189 saw_action ("activate");
190 }
191
192 static void
test_application_open(GApplication * application,GFile ** files,gint n_files,const gchar * hint)193 test_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 ("file:///a/b");
204 g_assert (g_file_equal (files[0], f));
205 g_object_unref (f);
206 f = g_file_new_for_uri ("file:///c/d");
207 g_assert (g_file_equal (files[1], f));
208 g_object_unref (f);
209
210 saw_action ("open");
211 }
212
213 static void
test_application_startup(GApplication * application)214 test_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), app_actions, G_N_ELEMENTS (app_actions), application);
220 }
221
222 static void
test_application_before_emit(GApplication * application,GVariant * platform_data)223 test_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 (platform_data, "desktop-startup-id", "&s", &startup_id))
231 return;
232
233 g_assert_cmpstr (startup_id, ==, "expected startup id");
234 saw_startup_id = TRUE;
235 }
236
237 static void
test_application_init(TestApplication * app)238 test_application_init (TestApplication *app)
239 {
240 }
241
242 static void
test_application_class_init(GApplicationClass * class)243 test_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
251 static void
test_dbus_appinfo(void)252 test_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 (G_TEST_DIST,
260 "org.gtk.test.dbusappinfo.desktop",
261 NULL);
262 appinfo = g_desktop_app_info_new_from_filename (desktop_file);
263 g_assert (appinfo != NULL);
264 g_free (desktop_file);
265
266 app = g_object_new (test_application_get_type (),
267 "application-id", "org.gtk.test.dbusappinfo",
268 "flags", G_APPLICATION_HANDLES_OPEN,
269 NULL);
270 status = g_application_run (app, 1, (gchar **) argv);
271
272 g_assert_cmpint (status, ==, 0);
273 g_assert_cmpint (current_state, ==, 8);
274
275 g_object_unref (appinfo);
276 g_object_unref (app);
277 }
278
279 static void
on_flatpak_launch_uris_finish(GObject * object,GAsyncResult * result,gpointer user_data)280 on_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);
288 g_assert_no_error (error);
289
290 g_application_release (app);
291 }
292
293 static void
on_flatpak_activate(GApplication * app,gpointer user_data)294 on_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 (app);
303
304 uri = g_filename_to_uri (g_desktop_app_info_get_filename (flatpak_appinfo), NULL, NULL);
305 g_assert_nonnull (uri);
306 uris = g_list_prepend (NULL, uri);
307 g_app_info_launch_uris_async (G_APP_INFO (flatpak_appinfo), uris, NULL,
308 NULL, on_flatpak_launch_uris_finish, app);
309 g_list_free (uris);
310 g_free (uri);
311 }
312
313 static void
on_flatpak_open(GApplication * app,GFile ** files,gint n_files,const char * hint)314 on_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 ("on_flatpak_open received file '%s'", g_file_peek_path (files[0]));
323
324 /* The file has been exported via the document portal */
325 f = g_file_new_for_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 (f);
328 }
329
330 static void
test_flatpak_doc_export(void)331 test_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 ("Test that files launched via Flatpak apps are made available via the document portal.");
340
341 desktop_file = g_test_build_filename (G_TEST_DIST,
342 "org.gtk.test.dbusappinfo.flatpak.desktop",
343 NULL);
344 flatpak_appinfo = g_desktop_app_info_new_from_filename (desktop_file);
345 g_assert_nonnull (flatpak_appinfo);
346 g_free (desktop_file);
347
348 app = g_application_new ("org.gtk.test.dbusappinfo.flatpak",
349 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 (app, 1, (gchar **) argv);
355 g_assert_cmpint (status, ==, 0);
356
357 g_object_unref (app);
358 g_object_unref (flatpak_appinfo);
359 }
360
361 int
main(int argc,char ** argv)362 main (int argc, char **argv)
363 {
364 g_test_init (&argc, &argv, NULL);
365
366 g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo);
367 g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export);
368
369 return session_bus_run ();
370 }
371