Add tests for remote actions
[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 static void
238 test_remote_actions (void)
239 {
240   GDBusConnection *c;
241
242   g_assert (outstanding_watches == 0);
243
244   session_bus_up ();
245   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
246
247   main_loop = g_main_loop_new (NULL, 0);
248
249   /* spawn the master */
250   spawn ("got ./cmd 0\n"
251          "activate action1\n"
252          "change action2 1\n"
253          "exit status: 0\n", NULL,
254          "./cmd", NULL);
255
256   spawn ("actions quit new action1 action2\n"
257          "exit status: 0\n", NULL,
258          "./actions", "list", NULL);
259
260   spawn ("exit status: 0\n", NULL,
261          "./actions", "activate", NULL);
262
263   spawn ("exit status: 0\n", NULL,
264          "./actions", "set-state", NULL);
265
266   g_main_loop_run (main_loop);
267
268   g_object_unref (c);
269   session_bus_down ();
270
271   g_main_loop_unref (main_loop);
272 }
273
274 #if 0
275 /* Now that we register non-unique apps on the bus we need to fix the
276  * following test not to assume that it's safe to create multiple instances
277  * of the same app in one process.
278  *
279  * See https://bugzilla.gnome.org/show_bug.cgi?id=647986 for the patch that
280  * introduced this problem.
281  */
282
283 static GApplication *recently_activated;
284 static GMainLoop *loop;
285
286 static void
287 nonunique_activate (GApplication *application)
288 {
289   recently_activated = application;
290
291   if (loop != NULL)
292     g_main_loop_quit (loop);
293 }
294
295 static GApplication *
296 make_app (gboolean non_unique)
297 {
298   GApplication *app;
299   gboolean ok;
300
301   app = g_application_new ("org.gtk.Test-Application",
302                            non_unique ? G_APPLICATION_NON_UNIQUE : 0);
303   g_signal_connect (app, "activate", G_CALLBACK (nonunique_activate), NULL);
304   ok = g_application_register (app, NULL, NULL);
305   if (!ok)
306     {
307       g_object_unref (app);
308       return NULL;
309     }
310
311   g_application_activate (app);
312
313   return app;
314 }
315
316 static void
317 test_nonunique (void)
318 {
319   GApplication *first, *second, *third, *fourth;
320
321   session_bus_up ();
322
323   first = make_app (TRUE);
324   /* non-remote because it is non-unique */
325   g_assert (!g_application_get_is_remote (first));
326   g_assert (recently_activated == first);
327   recently_activated = NULL;
328
329   second = make_app (FALSE);
330   /* non-remote because it is first */
331   g_assert (!g_application_get_is_remote (second));
332   g_assert (recently_activated == second);
333   recently_activated = NULL;
334
335   third = make_app (TRUE);
336   /* non-remote because it is non-unique */
337   g_assert (!g_application_get_is_remote (third));
338   g_assert (recently_activated == third);
339   recently_activated = NULL;
340
341   fourth = make_app (FALSE);
342   /* should have failed to register due to being
343    * unable to register the object paths
344    */
345   g_assert (fourth == NULL);
346   g_assert (recently_activated == NULL);
347
348   g_object_unref (first);
349   g_object_unref (second);
350   g_object_unref (third);
351
352   session_bus_down ();
353 }
354 #endif
355
356 static void
357 properties (void)
358 {
359   GDBusConnection *c;
360   GObject *app;
361   gchar *id;
362   GApplicationFlags flags;
363   gboolean registered;
364   guint timeout;
365   gboolean remote;
366   gboolean ret;
367   GError *error = NULL;
368
369   session_bus_up ();
370   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
371
372   app = g_object_new (G_TYPE_APPLICATION,
373                       "application-id", "org.gtk.TestApplication",
374                       NULL);
375
376   g_object_get (app,
377                 "application-id", &id,
378                 "flags", &flags,
379                 "is-registered", &registered,
380                 "inactivity-timeout", &timeout,
381                 NULL);
382
383   g_assert_cmpstr (id, ==, "org.gtk.TestApplication");
384   g_assert_cmpint (flags, ==, G_APPLICATION_FLAGS_NONE);
385   g_assert (!registered);
386   g_assert_cmpint (timeout, ==, 0);
387
388   ret = g_application_register (G_APPLICATION (app), NULL, &error);
389   g_assert (ret);
390   g_assert_no_error (error);
391
392   g_object_get (app,
393                 "is-registered", &registered,
394                 "is-remote", &remote,
395                 NULL);
396
397   g_assert (registered);
398   g_assert (!remote);
399
400   g_object_set (app,
401                 "inactivity-timeout", 1000,
402                 NULL);
403
404   g_application_quit (G_APPLICATION (app));
405
406   g_object_unref (c);
407   g_object_unref (app);
408   g_free (id);
409
410   session_bus_down ();
411 }
412
413 static void
414 appid (void)
415 {
416   gchar *id;
417
418   g_assert (!g_application_id_is_valid (""));
419   g_assert (!g_application_id_is_valid ("."));
420   g_assert (!g_application_id_is_valid ("a"));
421   g_assert (!g_application_id_is_valid ("abc"));
422   g_assert (!g_application_id_is_valid (".abc"));
423   g_assert (!g_application_id_is_valid ("abc."));
424   g_assert (!g_application_id_is_valid ("a..b"));
425   g_assert (!g_application_id_is_valid ("a/b"));
426   g_assert (!g_application_id_is_valid ("a\nb"));
427   g_assert (!g_application_id_is_valid ("a\nb"));
428   g_assert (!g_application_id_is_valid ("_a.b"));
429   g_assert (!g_application_id_is_valid ("-a.b"));
430   id = g_new0 (gchar, 261);
431   memset (id, 'a', 260);
432   id[1] = '.';
433   id[260] = 0;
434   g_assert (!g_application_id_is_valid (id));
435   g_free (id);
436
437   g_assert (g_application_id_is_valid ("a.b"));
438   g_assert (g_application_id_is_valid ("A.B"));
439   g_assert (g_application_id_is_valid ("A-.B"));
440   g_assert (g_application_id_is_valid ("a_b.c-d"));
441   g_assert (g_application_id_is_valid ("org.gnome.SessionManager"));
442 }
443
444 static gboolean nodbus_activated;
445
446 static gboolean
447 release_app (gpointer user_data)
448 {
449   g_application_release (user_data);
450   return G_SOURCE_REMOVE;
451 }
452
453 static void
454 nodbus_activate (GApplication *app)
455 {
456   nodbus_activated = TRUE;
457   g_application_hold (app);
458
459   g_assert (g_application_get_dbus_connection (app) == NULL);
460   g_assert (g_application_get_dbus_object_path (app) == NULL);
461
462   g_idle_add (release_app, app);
463 }
464
465 static void
466 test_nodbus (void)
467 {
468   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
469   gchar *argv[] = { binpath, NULL };
470   GApplication *app;
471
472   app = g_application_new ("org.gtk.Unimportant", G_APPLICATION_FLAGS_NONE);
473   g_signal_connect (app, "activate", G_CALLBACK (nodbus_activate), NULL);
474   g_application_run (app, 1, argv);
475   g_object_unref (app);
476
477   g_assert (nodbus_activated);
478   g_free (binpath);
479 }
480
481 static gboolean noappid_activated;
482
483 static void
484 noappid_activate (GApplication *app)
485 {
486   noappid_activated = TRUE;
487   g_application_hold (app);
488
489   g_assert (g_application_get_flags (app) & G_APPLICATION_NON_UNIQUE);
490
491   g_idle_add (release_app, app);
492 }
493
494 /* test that no appid -> non-unique */
495 static void
496 test_noappid (void)
497 {
498   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
499   gchar *argv[] = { binpath, NULL };
500   GApplication *app;
501
502   app = g_application_new (NULL, G_APPLICATION_FLAGS_NONE);
503   g_signal_connect (app, "activate", G_CALLBACK (noappid_activate), NULL);
504   g_application_run (app, 1, argv);
505   g_object_unref (app);
506
507   g_assert (noappid_activated);
508   g_free (binpath);
509 }
510
511 static gboolean activated;
512 static gboolean quitted;
513
514 static gboolean
515 quit_app (gpointer user_data)
516 {
517   quitted = TRUE;
518   g_application_quit (user_data);
519   return G_SOURCE_REMOVE;
520 }
521
522 static void
523 quit_activate (GApplication *app)
524 {
525   activated = TRUE;
526   g_application_hold (app);
527
528   g_assert (g_application_get_dbus_connection (app) != NULL);
529   g_assert (g_application_get_dbus_object_path (app) != NULL);
530
531   g_idle_add (quit_app, app);
532 }
533
534 static void
535 test_quit (void)
536 {
537   GDBusConnection *c;
538   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
539   gchar *argv[] = { binpath, NULL };
540   GApplication *app;
541
542   session_bus_up ();
543   c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
544
545   app = g_application_new ("org.gtk.Unimportant",
546                            G_APPLICATION_FLAGS_NONE);
547   activated = FALSE;
548   quitted = FALSE;
549   g_signal_connect (app, "activate", G_CALLBACK (quit_activate), NULL);
550   g_application_run (app, 1, argv);
551   g_object_unref (app);
552   g_object_unref (c);
553
554   g_assert (activated);
555   g_assert (quitted);
556
557   session_bus_down ();
558   g_free (binpath);
559 }
560
561 static void
562 on_activate (GApplication *app)
563 {
564   gchar **actions;
565   GAction *action;
566   GVariant *state;
567
568   g_assert (!g_application_get_is_remote (app));
569
570   actions = g_action_group_list_actions (G_ACTION_GROUP (app));
571   g_assert (g_strv_length (actions) == 0);
572   g_strfreev (actions);
573
574   action = (GAction*)g_simple_action_new_stateful ("test", G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean (FALSE));
575   g_action_map_add_action (G_ACTION_MAP (app), action);
576
577   actions = g_action_group_list_actions (G_ACTION_GROUP (app));
578   g_assert (g_strv_length (actions) == 1);
579   g_strfreev (actions);
580
581   g_action_group_change_action_state (G_ACTION_GROUP (app), "test", g_variant_new_boolean (TRUE));
582   state = g_action_group_get_action_state (G_ACTION_GROUP (app), "test");
583   g_assert (g_variant_get_boolean (state) == TRUE);
584
585   action = g_action_map_lookup_action (G_ACTION_MAP (app), "test");
586   g_assert (action != NULL);
587
588   g_action_map_remove_action (G_ACTION_MAP (app), "test");
589
590   actions = g_action_group_list_actions (G_ACTION_GROUP (app));
591   g_assert (g_strv_length (actions) == 0);
592   g_strfreev (actions);
593 }
594
595 static void
596 test_local_actions (void)
597 {
598   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
599   gchar *argv[] = { binpath, NULL };
600   GApplication *app;
601
602   app = g_application_new ("org.gtk.Unimportant",
603                            G_APPLICATION_FLAGS_NONE);
604   g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
605   g_application_run (app, 1, argv);
606   g_object_unref (app);
607   g_free (binpath);
608 }
609
610 typedef GApplication TestLocCmdApp;
611 typedef GApplicationClass TestLocCmdAppClass;
612
613 static GType test_loc_cmd_app_get_type (void);
614 G_DEFINE_TYPE (TestLocCmdApp, test_loc_cmd_app, G_TYPE_APPLICATION)
615
616 static void
617 test_loc_cmd_app_init (TestLocCmdApp *app)
618 {
619 }
620
621 static void
622 test_loc_cmd_app_startup (GApplication *app)
623 {
624   g_assert_not_reached ();
625 }
626
627 static void
628 test_loc_cmd_app_shutdown (GApplication *app)
629 {
630   g_assert_not_reached ();
631 }
632
633 static gboolean
634 test_loc_cmd_app_local_command_line (GApplication   *application,
635                                      gchar        ***arguments,
636                                      gint           *exit_status)
637 {
638   return TRUE;
639 }
640
641 static void
642 test_loc_cmd_app_class_init (TestLocCmdAppClass *klass)
643 {
644   G_APPLICATION_CLASS (klass)->startup = test_loc_cmd_app_startup;
645   G_APPLICATION_CLASS (klass)->shutdown = test_loc_cmd_app_shutdown;
646   G_APPLICATION_CLASS (klass)->local_command_line = test_loc_cmd_app_local_command_line;
647 }
648
649 static void
650 test_local_command_line (void)
651 {
652   char *binpath = g_test_build_filename (G_TEST_BUILT, "unimportant", NULL);
653   gchar *argv[] = { binpath, "-invalid", NULL };
654   GApplication *app;
655
656   app = g_object_new (test_loc_cmd_app_get_type (),
657                       "application-id", "org.gtk.Unimportant",
658                       "flags", G_APPLICATION_FLAGS_NONE,
659                       NULL);
660   g_application_run (app, 1, argv);
661   g_object_unref (app);
662   g_free (binpath);
663 }
664
665 int
666 main (int argc, char **argv)
667 {
668   g_test_init (&argc, &argv, NULL);
669
670   g_test_dbus_unset ();
671
672   g_test_add_func ("/gapplication/no-dbus", test_nodbus);
673   g_test_add_func ("/gapplication/basic", basic);
674   g_test_add_func ("/gapplication/no-appid", test_noappid);
675 /*  g_test_add_func ("/gapplication/non-unique", test_nonunique); */
676   g_test_add_func ("/gapplication/properties", properties);
677   g_test_add_func ("/gapplication/app-id", appid);
678   g_test_add_func ("/gapplication/quit", test_quit);
679   g_test_add_func ("/gapplication/local-actions", test_local_actions);
680   g_test_add_func ("/gapplication/remote-actions", test_remote_actions);
681   g_test_add_func ("/gapplication/local-command-line", test_local_command_line);
682   g_test_add_func ("/gapplication/remote-command-line", test_remote_command_line);
683
684   return g_test_run ();
685 }