Improve GApplicationCommandline test coverage
[platform/upstream/glib.git] / gio / tests / gapplication.c
1 #include <gio/gio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5
6 #include "gdbus-tests.h"
7 #include "gdbus-sessionbus.h"
8
9 static gint outstanding_watches;
10 static GMainLoop *main_loop;
11
12 typedef struct
13 {
14   gchar *expected_stdout;
15   gint stdout_pipe;
16   gchar *expected_stderr;
17   gint stderr_pipe;
18 } ChildData;
19
20 static void
21 check_data (gint fd, const gchar *expected)
22 {
23   gssize len, actual;
24   gchar *buffer;
25   
26   len = strlen (expected);
27   buffer = g_alloca (len + 100);
28   actual = read (fd, buffer, len + 100);
29
30   g_assert_cmpint (actual, >=, 0);
31
32   if (actual != len ||
33       memcmp (buffer, expected, len) != 0)
34     {
35       buffer[MIN(len + 100, actual)] = '\0';
36
37       g_error ("\nExpected\n-----\n%s-----\nGot (%s)\n-----\n%s-----\n",
38                expected,
39                (actual > len) ? "truncated" : "full", buffer);
40     }
41 }
42
43 static void
44 child_quit (GPid     pid,
45             gint     status,
46             gpointer data)
47 {
48   ChildData *child = data;
49
50   g_assert_cmpint (status, ==, 0);
51
52   if (--outstanding_watches == 0)
53     g_main_loop_quit (main_loop);
54
55   check_data (child->stdout_pipe, child->expected_stdout);
56   close (child->stdout_pipe);
57   g_free (child->expected_stdout);
58
59   if (child->expected_stderr)
60     {
61       check_data (child->stderr_pipe, child->expected_stderr);
62       close (child->stderr_pipe);
63       g_free (child->expected_stderr);
64     }
65
66   g_slice_free (ChildData, child);
67 }
68
69 static void
70 spawn (const gchar *expected_stdout,
71        const gchar *expected_stderr,
72        const gchar *first_arg,
73        ...)
74 {
75   GError *error = NULL;
76   const gchar *arg;
77   GPtrArray *array;
78   ChildData *data;
79   gchar **args;
80   va_list ap;
81   GPid pid;
82   GPollFD fd;
83   gchar **env;
84
85   va_start (ap, first_arg);
86   array = g_ptr_array_new ();
87   g_ptr_array_add (array, g_test_build_filename (G_TEST_BUILT, "basic-application", NULL));
88   for (arg = first_arg; arg; arg = va_arg (ap, const gchar *))
89     g_ptr_array_add (array, g_strdup (arg));
90   g_ptr_array_add (array, NULL);
91   args = (gchar **) g_ptr_array_free (array, FALSE);
92   va_end (ap);
93
94   env = g_environ_setenv (g_get_environ (), "TEST", "1", TRUE);
95
96   data = g_slice_new (ChildData);
97   data->expected_stdout = g_strdup (expected_stdout);
98   data->expected_stderr = g_strdup (expected_stderr);
99
100   g_spawn_async_with_pipes (NULL, args, env,
101                             G_SPAWN_DO_NOT_REAP_CHILD,
102                             NULL, NULL, &pid, NULL,
103                             &data->stdout_pipe,
104                             expected_stderr ? &data->stderr_pipe : NULL,
105                             &error);
106   g_assert_no_error (error);
107
108   g_strfreev (env);
109
110   g_child_watch_add (pid, child_quit, data);
111   outstanding_watches++;
112
113   /* we block until the children write to stdout to make sure
114    * they have started, as they need to be executed in order;
115    * see https://bugzilla.gnome.org/show_bug.cgi?id=664627
116    */
117   fd.fd = data->stdout_pipe;
118   fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
119   g_poll (&fd, 1, -1);
120 }
121
122 static void
123 basic (void)
124 {
125   GDBusConnection *c;
126
127   g_assert (outstanding_watches == 0);
128
129   session_bus_up ();
130   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
131
132   main_loop = g_main_loop_new (NULL, 0);
133
134   /* spawn the master */
135   spawn ("activated\n"
136          "open file:///a file:///b\n"
137          "exit status: 0\n", NULL,
138          "./app", NULL);
139
140   /* send it some files */
141   spawn ("exit status: 0\n", NULL,
142          "./app", "/a", "/b", NULL);
143
144   g_main_loop_run (main_loop);
145
146   g_object_unref (c);
147   session_bus_down ();
148
149   g_main_loop_unref (main_loop);
150 }
151
152 static void
153 test_remote_command_line (void)
154 {
155   GDBusConnection *c;
156   GFile *file;
157   gchar *replies;
158   gchar *cwd;
159
160   g_assert (outstanding_watches == 0);
161
162   session_bus_up ();
163   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
164
165   main_loop = g_main_loop_new (NULL, 0);
166
167   file = g_file_new_for_commandline_arg ("foo");
168   cwd = g_get_current_dir ();
169
170   replies = g_strconcat ("got ./cmd 0\n",
171                          "got ./cmd 1\n",
172                          "cmdline ./cmd echo --abc -d\n",
173                          "environment TEST=1\n",
174                          "getenv TEST=1\n",
175                          "file ", g_file_get_path (file), "\n",
176                          "properties ok\n",
177                          "cwd ", cwd, "\n",
178                          "busy\n",
179                          "idle\n",
180                          "stdin ok\n",        
181                          "exit status: 0\n",
182                          NULL);
183   g_object_unref (file);
184
185   /* spawn the master */
186   spawn (replies, NULL,
187          "./cmd", NULL);
188
189   g_free (replies);
190
191   /* send it a few commandlines */
192   spawn ("exit status: 0\n", NULL,
193          "./cmd", NULL);
194
195   spawn ("exit status: 0\n", NULL,
196          "./cmd", "echo", "--abc", "-d", NULL);
197
198   spawn ("exit status: 0\n", NULL,
199          "./cmd", "env", NULL);
200
201   spawn ("exit status: 0\n", NULL,
202          "./cmd", "getenv", NULL);
203
204   spawn ("print test\n"
205          "exit status: 0\n", NULL,
206          "./cmd", "print", "test", NULL);
207
208   spawn ("exit status: 0\n", "printerr test\n",
209          "./cmd", "printerr", "test", NULL);
210
211   spawn ("exit status: 0\n", NULL,
212          "./cmd", "file", "foo", NULL);
213
214   spawn ("exit status: 0\n", NULL,
215          "./cmd", "properties", NULL);
216
217   spawn ("exit status: 0\n", NULL,
218          "./cmd", "cwd", NULL);
219
220   spawn ("exit status: 0\n", NULL,
221          "./cmd", "busy", NULL);
222
223   spawn ("exit status: 0\n", NULL,
224          "./cmd", "idle", NULL);
225
226   spawn ("exit status: 0\n", NULL,
227          "./cmd", "stdin", NULL);
228
229   g_main_loop_run (main_loop);
230
231   g_object_unref (c);
232   session_bus_down ();
233
234   g_main_loop_unref (main_loop);
235 }
236
237 #if 0
238 /* Now that we register non-unique apps on the bus we need to fix the
239  * following test not to assume that it's safe to create multiple instances
240  * of the same app in one process.
241  *
242  * See https://bugzilla.gnome.org/show_bug.cgi?id=647986 for the patch that
243  * introduced this problem.
244  */
245
246 static GApplication *recently_activated;
247 static GMainLoop *loop;
248
249 static void
250 nonunique_activate (GApplication *application)
251 {
252   recently_activated = application;
253
254   if (loop != NULL)
255     g_main_loop_quit (loop);
256 }
257
258 static GApplication *
259 make_app (gboolean non_unique)
260 {
261   GApplication *app;
262   gboolean ok;
263
264   app = g_application_new ("org.gtk.Test-Application",
265                            non_unique ? G_APPLICATION_NON_UNIQUE : 0);
266   g_signal_connect (app, "activate", G_CALLBACK (nonunique_activate), NULL);
267   ok = g_application_register (app, NULL, NULL);
268   if (!ok)
269     {
270       g_object_unref (app);
271       return NULL;
272     }
273
274   g_application_activate (app);
275
276   return app;
277 }
278
279 static void
280 test_nonunique (void)
281 {
282   GApplication *first, *second, *third, *fourth;
283
284   session_bus_up ();
285
286   first = make_app (TRUE);
287   /* non-remote because it is non-unique */
288   g_assert (!g_application_get_is_remote (first));
289   g_assert (recently_activated == first);
290   recently_activated = NULL;
291
292   second = make_app (FALSE);
293   /* non-remote because it is first */
294   g_assert (!g_application_get_is_remote (second));
295   g_assert (recently_activated == second);
296   recently_activated = NULL;
297
298   third = make_app (TRUE);
299   /* non-remote because it is non-unique */
300   g_assert (!g_application_get_is_remote (third));
301   g_assert (recently_activated == third);
302   recently_activated = NULL;
303
304   fourth = make_app (FALSE);
305   /* should have failed to register due to being
306    * unable to register the object paths
307    */
308   g_assert (fourth == NULL);
309   g_assert (recently_activated == NULL);
310
311   g_object_unref (first);
312   g_object_unref (second);
313   g_object_unref (third);
314
315   session_bus_down ();
316 }
317 #endif
318
319 static void
320 properties (void)
321 {
322   GDBusConnection *c;
323   GObject *app;
324   gchar *id;
325   GApplicationFlags flags;
326   gboolean registered;
327   guint timeout;
328   gboolean remote;
329   gboolean ret;
330   GError *error = NULL;
331
332   session_bus_up ();
333   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
334
335   app = g_object_new (G_TYPE_APPLICATION,
336                       "application-id", "org.gtk.TestApplication",
337                       NULL);
338
339   g_object_get (app,
340                 "application-id", &id,
341                 "flags", &flags,
342                 "is-registered", &registered,
343                 "inactivity-timeout", &timeout,
344                 NULL);
345
346   g_assert_cmpstr (id, ==, "org.gtk.TestApplication");
347   g_assert_cmpint (flags, ==, G_APPLICATION_FLAGS_NONE);
348   g_assert (!registered);
349   g_assert_cmpint (timeout, ==, 0);
350
351   ret = g_application_register (G_APPLICATION (app), NULL, &error);
352   g_assert (ret);
353   g_assert_no_error (error);
354
355   g_object_get (app,
356                 "is-registered", &registered,
357                 "is-remote", &remote,
358                 NULL);
359
360   g_assert (registered);
361   g_assert (!remote);
362
363   g_object_set (app,
364                 "inactivity-timeout", 1000,
365                 NULL);
366
367   g_application_quit (G_APPLICATION (app));
368
369   g_object_unref (c);
370   g_object_unref (app);
371   g_free (id);
372
373   session_bus_down ();
374 }
375
376 static void
377 appid (void)
378 {
379   gchar *id;
380
381   g_assert (!g_application_id_is_valid (""));
382   g_assert (!g_application_id_is_valid ("."));
383   g_assert (!g_application_id_is_valid ("a"));
384   g_assert (!g_application_id_is_valid ("abc"));
385   g_assert (!g_application_id_is_valid (".abc"));
386   g_assert (!g_application_id_is_valid ("abc."));
387   g_assert (!g_application_id_is_valid ("a..b"));
388   g_assert (!g_application_id_is_valid ("a/b"));
389   g_assert (!g_application_id_is_valid ("a\nb"));
390   g_assert (!g_application_id_is_valid ("a\nb"));
391   g_assert (!g_application_id_is_valid ("_a.b"));
392   g_assert (!g_application_id_is_valid ("-a.b"));
393   id = g_new0 (gchar, 261);
394   memset (id, 'a', 260);
395   id[1] = '.';
396   id[260] = 0;
397   g_assert (!g_application_id_is_valid (id));
398   g_free (id);
399
400   g_assert (g_application_id_is_valid ("a.b"));
401   g_assert (g_application_id_is_valid ("A.B"));
402   g_assert (g_application_id_is_valid ("A-.B"));
403   g_assert (g_application_id_is_valid ("a_b.c-d"));
404   g_assert (g_application_id_is_valid ("org.gnome.SessionManager"));
405 }
406
407 static gboolean nodbus_activated;
408
409 static gboolean
410 release_app (gpointer user_data)
411 {
412   g_application_release (user_data);
413   return G_SOURCE_REMOVE;
414 }
415
416 static void
417 nodbus_activate (GApplication *app)
418 {
419   nodbus_activated = TRUE;
420   g_application_hold (app);
421
422   g_assert (g_application_get_dbus_connection (app) == NULL);
423   g_assert (g_application_get_dbus_object_path (app) == NULL);
424
425   g_idle_add (release_app, app);
426 }
427
428 static void
429 test_nodbus (void)
430 {
431   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
432   gchar *argv[] = { binpath, NULL };
433   GApplication *app;
434
435   app = g_application_new ("org.gtk.Unimportant", G_APPLICATION_FLAGS_NONE);
436   g_signal_connect (app, "activate", G_CALLBACK (nodbus_activate), NULL);
437   g_application_run (app, 1, argv);
438   g_object_unref (app);
439
440   g_assert (nodbus_activated);
441   g_free (binpath);
442 }
443
444 static gboolean noappid_activated;
445
446 static void
447 noappid_activate (GApplication *app)
448 {
449   noappid_activated = TRUE;
450   g_application_hold (app);
451
452   g_assert (g_application_get_flags (app) & G_APPLICATION_NON_UNIQUE);
453
454   g_idle_add (release_app, app);
455 }
456
457 /* test that no appid -> non-unique */
458 static void
459 test_noappid (void)
460 {
461   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
462   gchar *argv[] = { binpath, NULL };
463   GApplication *app;
464
465   app = g_application_new (NULL, G_APPLICATION_FLAGS_NONE);
466   g_signal_connect (app, "activate", G_CALLBACK (noappid_activate), NULL);
467   g_application_run (app, 1, argv);
468   g_object_unref (app);
469
470   g_assert (noappid_activated);
471   g_free (binpath);
472 }
473
474 static gboolean activated;
475 static gboolean quitted;
476
477 static gboolean
478 quit_app (gpointer user_data)
479 {
480   quitted = TRUE;
481   g_application_quit (user_data);
482   return G_SOURCE_REMOVE;
483 }
484
485 static void
486 quit_activate (GApplication *app)
487 {
488   activated = TRUE;
489   g_application_hold (app);
490
491   g_assert (g_application_get_dbus_connection (app) != NULL);
492   g_assert (g_application_get_dbus_object_path (app) != NULL);
493
494   g_idle_add (quit_app, app);
495 }
496
497 static void
498 test_quit (void)
499 {
500   GDBusConnection *c;
501   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
502   gchar *argv[] = { binpath, NULL };
503   GApplication *app;
504
505   session_bus_up ();
506   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
507
508   app = g_application_new ("org.gtk.Unimportant",
509                            G_APPLICATION_FLAGS_NONE);
510   activated = FALSE;
511   quitted = FALSE;
512   g_signal_connect (app, "activate", G_CALLBACK (quit_activate), NULL);
513   g_application_run (app, 1, argv);
514   g_object_unref (app);
515   g_object_unref (c);
516
517   g_assert (activated);
518   g_assert (quitted);
519
520   session_bus_down ();
521   g_free (binpath);
522 }
523
524 static void
525 on_activate (GApplication *app)
526 {
527   gchar **actions;
528   GAction *action;
529   GVariant *state;
530
531   g_assert (!g_application_get_is_remote (app));
532
533   actions = g_action_group_list_actions (G_ACTION_GROUP (app));
534   g_assert (g_strv_length (actions) == 0);
535   g_strfreev (actions);
536
537   action = (GAction*)g_simple_action_new_stateful ("test", G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean (FALSE));
538   g_action_map_add_action (G_ACTION_MAP (app), action);
539
540   actions = g_action_group_list_actions (G_ACTION_GROUP (app));
541   g_assert (g_strv_length (actions) == 1);
542   g_strfreev (actions);
543
544   g_action_group_change_action_state (G_ACTION_GROUP (app), "test", g_variant_new_boolean (TRUE));
545   state = g_action_group_get_action_state (G_ACTION_GROUP (app), "test");
546   g_assert (g_variant_get_boolean (state) == TRUE);
547
548   action = g_action_map_lookup_action (G_ACTION_MAP (app), "test");
549   g_assert (action != NULL);
550
551   g_action_map_remove_action (G_ACTION_MAP (app), "test");
552
553   actions = g_action_group_list_actions (G_ACTION_GROUP (app));
554   g_assert (g_strv_length (actions) == 0);
555   g_strfreev (actions);
556 }
557
558 static void
559 test_actions (void)
560 {
561   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
562   gchar *argv[] = { binpath, NULL };
563   GApplication *app;
564
565   app = g_application_new ("org.gtk.Unimportant",
566                            G_APPLICATION_FLAGS_NONE);
567   g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
568   g_application_run (app, 1, argv);
569   g_object_unref (app);
570   g_free (binpath);
571 }
572
573 typedef GApplication TestLocCmdApp;
574 typedef GApplicationClass TestLocCmdAppClass;
575
576 static GType test_loc_cmd_app_get_type (void);
577 G_DEFINE_TYPE (TestLocCmdApp, test_loc_cmd_app, G_TYPE_APPLICATION)
578
579 static void
580 test_loc_cmd_app_init (TestLocCmdApp *app)
581 {
582 }
583
584 static void
585 test_loc_cmd_app_startup (GApplication *app)
586 {
587   g_assert_not_reached ();
588 }
589
590 static void
591 test_loc_cmd_app_shutdown (GApplication *app)
592 {
593   g_assert_not_reached ();
594 }
595
596 static gboolean
597 test_loc_cmd_app_local_command_line (GApplication   *application,
598                                      gchar        ***arguments,
599                                      gint           *exit_status)
600 {
601   return TRUE;
602 }
603
604 static void
605 test_loc_cmd_app_class_init (TestLocCmdAppClass *klass)
606 {
607   G_APPLICATION_CLASS (klass)->startup = test_loc_cmd_app_startup;
608   G_APPLICATION_CLASS (klass)->shutdown = test_loc_cmd_app_shutdown;
609   G_APPLICATION_CLASS (klass)->local_command_line = test_loc_cmd_app_local_command_line;
610 }
611
612 static void
613 test_local_command_line (void)
614 {
615   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
616   gchar *argv[] = { binpath, "-invalid", NULL };
617   GApplication *app;
618
619   app = g_object_new (test_loc_cmd_app_get_type (),
620                       "application-id", "org.gtk.Unimportant",
621                       "flags", G_APPLICATION_FLAGS_NONE,
622                       NULL);
623   g_application_run (app, 1, argv);
624   g_object_unref (app);
625   g_free (binpath);
626 }
627
628 int
629 main (int argc, char **argv)
630 {
631   g_test_init (&argc, &argv, NULL);
632
633   g_test_dbus_unset ();
634
635   g_test_add_func ("/gapplication/no-dbus", test_nodbus);
636   g_test_add_func ("/gapplication/basic", basic);
637   g_test_add_func ("/gapplication/no-appid", test_noappid);
638 /*  g_test_add_func ("/gapplication/non-unique", test_nonunique); */
639   g_test_add_func ("/gapplication/properties", properties);
640   g_test_add_func ("/gapplication/app-id", appid);
641   g_test_add_func ("/gapplication/quit", test_quit);
642   g_test_add_func ("/gapplication/actions", test_actions);
643   g_test_add_func ("/gapplication/local-command-line", test_local_command_line);
644   g_test_add_func ("/gapplication/remote-command-line", test_remote_command_line);
645
646   return g_test_run ();
647 }