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 | #if 0 |
10 | /* These tests are racy -- there is no guarantee about the order of data |
11 | * arriving over D-Bus. |
12 | * |
13 | * They're also a bit ridiculous -- GApplication was never meant to be |
14 | * abused in this way... |
15 | * |
16 | * We need new tests. |
17 | */ |
18 | static gint outstanding_watches; |
19 | static GMainLoop *main_loop; |
20 | |
21 | typedef struct |
22 | { |
23 | gchar *expected_stdout; |
24 | gint stdout_pipe; |
25 | gchar *expected_stderr; |
26 | gint stderr_pipe; |
27 | } ChildData; |
28 | |
29 | static void |
30 | check_data (gint fd, const gchar *expected) |
31 | { |
32 | gssize len, actual; |
33 | gchar *buffer; |
34 | |
35 | len = strlen (expected); |
36 | buffer = g_alloca (len + 100); |
37 | actual = read (fd, buffer, len + 100); |
38 | |
39 | g_assert_cmpint (actual, >=, 0); |
40 | |
41 | if (actual != len || |
42 | memcmp (buffer, expected, len) != 0) |
43 | { |
44 | buffer[MIN(len + 100, actual)] = '\0'; |
45 | |
46 | g_error ("\nExpected\n-----\n%s-----\nGot (%s)\n-----\n%s-----\n" , |
47 | expected, |
48 | (actual > len) ? "truncated" : "full" , buffer); |
49 | } |
50 | } |
51 | |
52 | static void |
53 | child_quit (GPid pid, |
54 | gint status, |
55 | gpointer data) |
56 | { |
57 | ChildData *child = data; |
58 | |
59 | g_assert_cmpint (status, ==, 0); |
60 | |
61 | if (--outstanding_watches == 0) |
62 | g_main_loop_quit (main_loop); |
63 | |
64 | check_data (child->stdout_pipe, child->expected_stdout); |
65 | close (child->stdout_pipe); |
66 | g_free (child->expected_stdout); |
67 | |
68 | if (child->expected_stderr) |
69 | { |
70 | check_data (child->stderr_pipe, child->expected_stderr); |
71 | close (child->stderr_pipe); |
72 | g_free (child->expected_stderr); |
73 | } |
74 | |
75 | g_slice_free (ChildData, child); |
76 | } |
77 | |
78 | static void |
79 | spawn (const gchar *expected_stdout, |
80 | const gchar *expected_stderr, |
81 | const gchar *first_arg, |
82 | ...) |
83 | { |
84 | GError *error = NULL; |
85 | const gchar *arg; |
86 | GPtrArray *array; |
87 | ChildData *data; |
88 | gchar **args; |
89 | va_list ap; |
90 | GPid pid; |
91 | GPollFD fd; |
92 | gchar **env; |
93 | |
94 | va_start (ap, first_arg); |
95 | array = g_ptr_array_new (); |
96 | g_ptr_array_add (array, g_test_build_filename (G_TEST_BUILT, "basic-application" , NULL)); |
97 | for (arg = first_arg; arg; arg = va_arg (ap, const gchar *)) |
98 | g_ptr_array_add (array, g_strdup (arg)); |
99 | g_ptr_array_add (array, NULL); |
100 | args = (gchar **) g_ptr_array_free (array, FALSE); |
101 | va_end (ap); |
102 | |
103 | env = g_environ_setenv (g_get_environ (), "TEST" , "1" , TRUE); |
104 | |
105 | data = g_slice_new (ChildData); |
106 | data->expected_stdout = g_strdup (expected_stdout); |
107 | data->expected_stderr = g_strdup (expected_stderr); |
108 | |
109 | g_spawn_async_with_pipes (NULL, args, env, |
110 | G_SPAWN_DO_NOT_REAP_CHILD, |
111 | NULL, NULL, &pid, NULL, |
112 | &data->stdout_pipe, |
113 | expected_stderr ? &data->stderr_pipe : NULL, |
114 | &error); |
115 | g_assert_no_error (error); |
116 | |
117 | g_strfreev (env); |
118 | |
119 | g_child_watch_add (pid, child_quit, data); |
120 | outstanding_watches++; |
121 | |
122 | /* we block until the children write to stdout to make sure |
123 | * they have started, as they need to be executed in order; |
124 | * see https://bugzilla.gnome.org/show_bug.cgi?id=664627 |
125 | */ |
126 | fd.fd = data->stdout_pipe; |
127 | fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; |
128 | g_poll (&fd, 1, -1); |
129 | } |
130 | |
131 | static void |
132 | basic (void) |
133 | { |
134 | GDBusConnection *c; |
135 | |
136 | g_assert (outstanding_watches == 0); |
137 | |
138 | session_bus_up (); |
139 | c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); |
140 | |
141 | main_loop = g_main_loop_new (NULL, 0); |
142 | |
143 | /* spawn the main instance */ |
144 | spawn ("activated\n" |
145 | "open file:///a file:///b\n" |
146 | "exit status: 0\n" , NULL, |
147 | "./app" , NULL); |
148 | |
149 | /* send it some files */ |
150 | spawn ("exit status: 0\n" , NULL, |
151 | "./app" , "/a" , "/b" , NULL); |
152 | |
153 | g_main_loop_run (main_loop); |
154 | |
155 | g_object_unref (c); |
156 | session_bus_down (); |
157 | |
158 | g_main_loop_unref (main_loop); |
159 | } |
160 | |
161 | static void |
162 | test_remote_command_line (void) |
163 | { |
164 | GDBusConnection *c; |
165 | GFile *file; |
166 | gchar *replies; |
167 | gchar *cwd; |
168 | |
169 | g_assert (outstanding_watches == 0); |
170 | |
171 | session_bus_up (); |
172 | c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); |
173 | |
174 | main_loop = g_main_loop_new (NULL, 0); |
175 | |
176 | file = g_file_new_for_commandline_arg ("foo" ); |
177 | cwd = g_get_current_dir (); |
178 | |
179 | replies = g_strconcat ("got ./cmd 0\n" , |
180 | "got ./cmd 1\n" , |
181 | "cmdline ./cmd echo --abc -d\n" , |
182 | "environment TEST=1\n" , |
183 | "getenv TEST=1\n" , |
184 | "file " , g_file_get_path (file), "\n" , |
185 | "properties ok\n" , |
186 | "cwd " , cwd, "\n" , |
187 | "busy\n" , |
188 | "idle\n" , |
189 | "stdin ok\n" , |
190 | "exit status: 0\n" , |
191 | NULL); |
192 | g_object_unref (file); |
193 | |
194 | /* spawn the main instance */ |
195 | spawn (replies, NULL, |
196 | "./cmd" , NULL); |
197 | |
198 | g_free (replies); |
199 | |
200 | /* send it a few commandlines */ |
201 | spawn ("exit status: 0\n" , NULL, |
202 | "./cmd" , NULL); |
203 | |
204 | spawn ("exit status: 0\n" , NULL, |
205 | "./cmd" , "echo" , "--abc" , "-d" , NULL); |
206 | |
207 | spawn ("exit status: 0\n" , NULL, |
208 | "./cmd" , "env" , NULL); |
209 | |
210 | spawn ("exit status: 0\n" , NULL, |
211 | "./cmd" , "getenv" , NULL); |
212 | |
213 | spawn ("print test\n" |
214 | "exit status: 0\n" , NULL, |
215 | "./cmd" , "print" , "test" , NULL); |
216 | |
217 | spawn ("exit status: 0\n" , "printerr test\n" , |
218 | "./cmd" , "printerr" , "test" , NULL); |
219 | |
220 | spawn ("exit status: 0\n" , NULL, |
221 | "./cmd" , "file" , "foo" , NULL); |
222 | |
223 | spawn ("exit status: 0\n" , NULL, |
224 | "./cmd" , "properties" , NULL); |
225 | |
226 | spawn ("exit status: 0\n" , NULL, |
227 | "./cmd" , "cwd" , NULL); |
228 | |
229 | spawn ("exit status: 0\n" , NULL, |
230 | "./cmd" , "busy" , NULL); |
231 | |
232 | spawn ("exit status: 0\n" , NULL, |
233 | "./cmd" , "idle" , NULL); |
234 | |
235 | spawn ("exit status: 0\n" , NULL, |
236 | "./cmd" , "stdin" , NULL); |
237 | |
238 | g_main_loop_run (main_loop); |
239 | |
240 | g_object_unref (c); |
241 | session_bus_down (); |
242 | |
243 | g_main_loop_unref (main_loop); |
244 | } |
245 | |
246 | static void |
247 | test_remote_actions (void) |
248 | { |
249 | GDBusConnection *c; |
250 | |
251 | g_assert (outstanding_watches == 0); |
252 | |
253 | session_bus_up (); |
254 | c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); |
255 | |
256 | main_loop = g_main_loop_new (NULL, 0); |
257 | |
258 | /* spawn the main instance */ |
259 | spawn ("got ./cmd 0\n" |
260 | "activate action1\n" |
261 | "change action2 1\n" |
262 | "exit status: 0\n" , NULL, |
263 | "./cmd" , NULL); |
264 | |
265 | spawn ("actions quit new action1 action2\n" |
266 | "exit status: 0\n" , NULL, |
267 | "./actions" , "list" , NULL); |
268 | |
269 | spawn ("exit status: 0\n" , NULL, |
270 | "./actions" , "activate" , NULL); |
271 | |
272 | spawn ("exit status: 0\n" , NULL, |
273 | "./actions" , "set-state" , NULL); |
274 | |
275 | g_main_loop_run (main_loop); |
276 | |
277 | g_object_unref (c); |
278 | session_bus_down (); |
279 | |
280 | g_main_loop_unref (main_loop); |
281 | } |
282 | #endif |
283 | |
284 | #if 0 |
285 | /* Now that we register non-unique apps on the bus we need to fix the |
286 | * following test not to assume that it's safe to create multiple instances |
287 | * of the same app in one process. |
288 | * |
289 | * See https://bugzilla.gnome.org/show_bug.cgi?id=647986 for the patch that |
290 | * introduced this problem. |
291 | */ |
292 | |
293 | static GApplication *recently_activated; |
294 | static GMainLoop *loop; |
295 | |
296 | static void |
297 | nonunique_activate (GApplication *application) |
298 | { |
299 | recently_activated = application; |
300 | |
301 | if (loop != NULL) |
302 | g_main_loop_quit (loop); |
303 | } |
304 | |
305 | static GApplication * |
306 | make_app (gboolean non_unique) |
307 | { |
308 | GApplication *app; |
309 | gboolean ok; |
310 | |
311 | app = g_application_new ("org.gtk.Test-Application" , |
312 | non_unique ? G_APPLICATION_NON_UNIQUE : 0); |
313 | g_signal_connect (app, "activate" , G_CALLBACK (nonunique_activate), NULL); |
314 | ok = g_application_register (app, NULL, NULL); |
315 | if (!ok) |
316 | { |
317 | g_object_unref (app); |
318 | return NULL; |
319 | } |
320 | |
321 | g_application_activate (app); |
322 | |
323 | return app; |
324 | } |
325 | |
326 | static void |
327 | test_nonunique (void) |
328 | { |
329 | GApplication *first, *second, *third, *fourth; |
330 | |
331 | session_bus_up (); |
332 | |
333 | first = make_app (TRUE); |
334 | /* non-remote because it is non-unique */ |
335 | g_assert (!g_application_get_is_remote (first)); |
336 | g_assert (recently_activated == first); |
337 | recently_activated = NULL; |
338 | |
339 | second = make_app (FALSE); |
340 | /* non-remote because it is first */ |
341 | g_assert (!g_application_get_is_remote (second)); |
342 | g_assert (recently_activated == second); |
343 | recently_activated = NULL; |
344 | |
345 | third = make_app (TRUE); |
346 | /* non-remote because it is non-unique */ |
347 | g_assert (!g_application_get_is_remote (third)); |
348 | g_assert (recently_activated == third); |
349 | recently_activated = NULL; |
350 | |
351 | fourth = make_app (FALSE); |
352 | /* should have failed to register due to being |
353 | * unable to register the object paths |
354 | */ |
355 | g_assert (fourth == NULL); |
356 | g_assert (recently_activated == NULL); |
357 | |
358 | g_object_unref (first); |
359 | g_object_unref (second); |
360 | g_object_unref (third); |
361 | |
362 | session_bus_down (); |
363 | } |
364 | #endif |
365 | |
366 | static void |
367 | properties (void) |
368 | { |
369 | GDBusConnection *c; |
370 | GObject *app; |
371 | gchar *id; |
372 | GApplicationFlags flags; |
373 | gboolean registered; |
374 | guint timeout; |
375 | gboolean remote; |
376 | gboolean ret; |
377 | GError *error = NULL; |
378 | |
379 | session_bus_up (); |
380 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
381 | |
382 | app = g_object_new (G_TYPE_APPLICATION, |
383 | first_property_name: "application-id" , "org.gtk.TestApplication" , |
384 | NULL); |
385 | |
386 | g_object_get (object: app, |
387 | first_property_name: "application-id" , &id, |
388 | "flags" , &flags, |
389 | "is-registered" , ®istered, |
390 | "inactivity-timeout" , &timeout, |
391 | NULL); |
392 | |
393 | g_assert_cmpstr (id, ==, "org.gtk.TestApplication" ); |
394 | g_assert_cmpint (flags, ==, G_APPLICATION_FLAGS_NONE); |
395 | g_assert (!registered); |
396 | g_assert_cmpint (timeout, ==, 0); |
397 | |
398 | ret = g_application_register (G_APPLICATION (app), NULL, error: &error); |
399 | g_assert (ret); |
400 | g_assert_no_error (error); |
401 | |
402 | g_object_get (object: app, |
403 | first_property_name: "is-registered" , ®istered, |
404 | "is-remote" , &remote, |
405 | NULL); |
406 | |
407 | g_assert (registered); |
408 | g_assert (!remote); |
409 | |
410 | g_object_set (object: app, |
411 | first_property_name: "inactivity-timeout" , 1000, |
412 | NULL); |
413 | |
414 | g_application_quit (G_APPLICATION (app)); |
415 | |
416 | g_object_unref (object: c); |
417 | g_object_unref (object: app); |
418 | g_free (mem: id); |
419 | |
420 | session_bus_down (); |
421 | } |
422 | |
423 | static void |
424 | appid (void) |
425 | { |
426 | gchar *id; |
427 | |
428 | g_assert_false (g_application_id_is_valid ("" )); |
429 | g_assert_false (g_application_id_is_valid ("." )); |
430 | g_assert_false (g_application_id_is_valid ("a" )); |
431 | g_assert_false (g_application_id_is_valid ("abc" )); |
432 | g_assert_false (g_application_id_is_valid (".abc" )); |
433 | g_assert_false (g_application_id_is_valid ("abc." )); |
434 | g_assert_false (g_application_id_is_valid ("a..b" )); |
435 | g_assert_false (g_application_id_is_valid ("a/b" )); |
436 | g_assert_false (g_application_id_is_valid ("a\nb" )); |
437 | g_assert_false (g_application_id_is_valid ("a\nb" )); |
438 | g_assert_false (g_application_id_is_valid ("emoji_picker" )); |
439 | g_assert_false (g_application_id_is_valid ("emoji-picker" )); |
440 | g_assert_false (g_application_id_is_valid ("emojipicker" )); |
441 | g_assert_false (g_application_id_is_valid ("my.Terminal.0123" )); |
442 | id = g_new0 (gchar, 261); |
443 | memset (s: id, c: 'a', n: 260); |
444 | id[1] = '.'; |
445 | id[260] = 0; |
446 | g_assert_false (g_application_id_is_valid (id)); |
447 | g_free (mem: id); |
448 | |
449 | g_assert_true (g_application_id_is_valid ("a.b" )); |
450 | g_assert_true (g_application_id_is_valid ("A.B" )); |
451 | g_assert_true (g_application_id_is_valid ("A-.B" )); |
452 | g_assert_true (g_application_id_is_valid ("a_b.c-d" )); |
453 | g_assert_true (g_application_id_is_valid ("_a.b" )); |
454 | g_assert_true (g_application_id_is_valid ("-a.b" )); |
455 | g_assert_true (g_application_id_is_valid ("org.gnome.SessionManager" )); |
456 | g_assert_true (g_application_id_is_valid ("my.Terminal._0123" )); |
457 | g_assert_true (g_application_id_is_valid ("com.example.MyApp" )); |
458 | g_assert_true (g_application_id_is_valid ("com.example.internal_apps.Calculator" )); |
459 | g_assert_true (g_application_id_is_valid ("org._7_zip.Archiver" )); |
460 | } |
461 | |
462 | static gboolean nodbus_activated; |
463 | |
464 | static gboolean |
465 | release_app (gpointer user_data) |
466 | { |
467 | g_application_release (application: user_data); |
468 | return G_SOURCE_REMOVE; |
469 | } |
470 | |
471 | static void |
472 | nodbus_activate (GApplication *app) |
473 | { |
474 | nodbus_activated = TRUE; |
475 | g_application_hold (application: app); |
476 | |
477 | g_assert (g_application_get_dbus_connection (app) == NULL); |
478 | g_assert (g_application_get_dbus_object_path (app) == NULL); |
479 | |
480 | g_idle_add (function: release_app, data: app); |
481 | } |
482 | |
483 | static void |
484 | test_nodbus (void) |
485 | { |
486 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
487 | gchar *argv[] = { binpath, NULL }; |
488 | GApplication *app; |
489 | |
490 | app = g_application_new (application_id: "org.gtk.Unimportant" , flags: G_APPLICATION_FLAGS_NONE); |
491 | g_signal_connect (app, "activate" , G_CALLBACK (nodbus_activate), NULL); |
492 | g_application_run (application: app, argc: 1, argv); |
493 | g_object_unref (object: app); |
494 | |
495 | g_assert (nodbus_activated); |
496 | g_free (mem: binpath); |
497 | } |
498 | |
499 | static gboolean noappid_activated; |
500 | |
501 | static void |
502 | noappid_activate (GApplication *app) |
503 | { |
504 | noappid_activated = TRUE; |
505 | g_application_hold (application: app); |
506 | |
507 | g_assert (g_application_get_flags (app) & G_APPLICATION_NON_UNIQUE); |
508 | |
509 | g_idle_add (function: release_app, data: app); |
510 | } |
511 | |
512 | /* test that no appid -> non-unique */ |
513 | static void |
514 | test_noappid (void) |
515 | { |
516 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
517 | gchar *argv[] = { binpath, NULL }; |
518 | GApplication *app; |
519 | |
520 | app = g_application_new (NULL, flags: G_APPLICATION_FLAGS_NONE); |
521 | g_signal_connect (app, "activate" , G_CALLBACK (noappid_activate), NULL); |
522 | g_application_run (application: app, argc: 1, argv); |
523 | g_object_unref (object: app); |
524 | |
525 | g_assert (noappid_activated); |
526 | g_free (mem: binpath); |
527 | } |
528 | |
529 | static gboolean activated; |
530 | static gboolean quitted; |
531 | |
532 | static gboolean |
533 | quit_app (gpointer user_data) |
534 | { |
535 | quitted = TRUE; |
536 | g_application_quit (application: user_data); |
537 | return G_SOURCE_REMOVE; |
538 | } |
539 | |
540 | static void |
541 | quit_activate (GApplication *app) |
542 | { |
543 | activated = TRUE; |
544 | g_application_hold (application: app); |
545 | |
546 | g_assert (g_application_get_dbus_connection (app) != NULL); |
547 | g_assert (g_application_get_dbus_object_path (app) != NULL); |
548 | |
549 | g_idle_add (function: quit_app, data: app); |
550 | } |
551 | |
552 | static void |
553 | test_quit (void) |
554 | { |
555 | GDBusConnection *c; |
556 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
557 | gchar *argv[] = { binpath, NULL }; |
558 | GApplication *app; |
559 | |
560 | session_bus_up (); |
561 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
562 | |
563 | app = g_application_new (application_id: "org.gtk.Unimportant" , |
564 | flags: G_APPLICATION_FLAGS_NONE); |
565 | activated = FALSE; |
566 | quitted = FALSE; |
567 | g_signal_connect (app, "activate" , G_CALLBACK (quit_activate), NULL); |
568 | g_application_run (application: app, argc: 1, argv); |
569 | g_object_unref (object: app); |
570 | g_object_unref (object: c); |
571 | |
572 | g_assert (activated); |
573 | g_assert (quitted); |
574 | |
575 | session_bus_down (); |
576 | g_free (mem: binpath); |
577 | } |
578 | |
579 | static void |
580 | on_activate (GApplication *app) |
581 | { |
582 | gchar **actions; |
583 | GAction *action; |
584 | GVariant *state; |
585 | |
586 | g_assert (!g_application_get_is_remote (app)); |
587 | |
588 | actions = g_action_group_list_actions (G_ACTION_GROUP (app)); |
589 | g_assert (g_strv_length (actions) == 0); |
590 | g_strfreev (str_array: actions); |
591 | |
592 | action = (GAction*)g_simple_action_new_stateful (name: "test" , G_VARIANT_TYPE_BOOLEAN, state: g_variant_new_boolean (FALSE)); |
593 | g_action_map_add_action (G_ACTION_MAP (app), action); |
594 | |
595 | actions = g_action_group_list_actions (G_ACTION_GROUP (app)); |
596 | g_assert (g_strv_length (actions) == 1); |
597 | g_strfreev (str_array: actions); |
598 | |
599 | g_action_group_change_action_state (G_ACTION_GROUP (app), action_name: "test" , value: g_variant_new_boolean (TRUE)); |
600 | state = g_action_group_get_action_state (G_ACTION_GROUP (app), action_name: "test" ); |
601 | g_assert (g_variant_get_boolean (state) == TRUE); |
602 | |
603 | action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name: "test" ); |
604 | g_assert (action != NULL); |
605 | |
606 | g_action_map_remove_action (G_ACTION_MAP (app), action_name: "test" ); |
607 | |
608 | actions = g_action_group_list_actions (G_ACTION_GROUP (app)); |
609 | g_assert (g_strv_length (actions) == 0); |
610 | g_strfreev (str_array: actions); |
611 | } |
612 | |
613 | static void |
614 | test_local_actions (void) |
615 | { |
616 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
617 | gchar *argv[] = { binpath, NULL }; |
618 | GApplication *app; |
619 | |
620 | app = g_application_new (application_id: "org.gtk.Unimportant" , |
621 | flags: G_APPLICATION_FLAGS_NONE); |
622 | g_signal_connect (app, "activate" , G_CALLBACK (on_activate), NULL); |
623 | g_application_run (application: app, argc: 1, argv); |
624 | g_object_unref (object: app); |
625 | g_free (mem: binpath); |
626 | } |
627 | |
628 | typedef GApplication TestLocCmdApp; |
629 | typedef GApplicationClass TestLocCmdAppClass; |
630 | |
631 | static GType test_loc_cmd_app_get_type (void); |
632 | G_DEFINE_TYPE (TestLocCmdApp, test_loc_cmd_app, G_TYPE_APPLICATION) |
633 | |
634 | static void |
635 | test_loc_cmd_app_init (TestLocCmdApp *app) |
636 | { |
637 | } |
638 | |
639 | static void |
640 | test_loc_cmd_app_startup (GApplication *app) |
641 | { |
642 | g_assert_not_reached (); |
643 | } |
644 | |
645 | static void |
646 | test_loc_cmd_app_shutdown (GApplication *app) |
647 | { |
648 | g_assert_not_reached (); |
649 | } |
650 | |
651 | static gboolean |
652 | test_loc_cmd_app_local_command_line (GApplication *application, |
653 | gchar ***arguments, |
654 | gint *exit_status) |
655 | { |
656 | return TRUE; |
657 | } |
658 | |
659 | static void |
660 | test_loc_cmd_app_class_init (TestLocCmdAppClass *klass) |
661 | { |
662 | G_APPLICATION_CLASS (klass)->startup = test_loc_cmd_app_startup; |
663 | G_APPLICATION_CLASS (klass)->shutdown = test_loc_cmd_app_shutdown; |
664 | G_APPLICATION_CLASS (klass)->local_command_line = test_loc_cmd_app_local_command_line; |
665 | } |
666 | |
667 | static void |
668 | test_local_command_line (void) |
669 | { |
670 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
671 | gchar *argv[] = { binpath, "-invalid" , NULL }; |
672 | GApplication *app; |
673 | |
674 | app = g_object_new (object_type: test_loc_cmd_app_get_type (), |
675 | first_property_name: "application-id" , "org.gtk.Unimportant" , |
676 | "flags" , G_APPLICATION_FLAGS_NONE, |
677 | NULL); |
678 | g_application_run (application: app, argc: 1, argv); |
679 | g_object_unref (object: app); |
680 | g_free (mem: binpath); |
681 | } |
682 | |
683 | static void |
684 | test_resource_path (void) |
685 | { |
686 | GApplication *app; |
687 | |
688 | app = g_application_new (application_id: "x.y.z" , flags: 0); |
689 | g_assert_cmpstr (g_application_get_resource_base_path (app), ==, "/x/y/z" ); |
690 | |
691 | /* this should not change anything */ |
692 | g_application_set_application_id (application: app, application_id: "a.b.c" ); |
693 | g_assert_cmpstr (g_application_get_resource_base_path (app), ==, "/x/y/z" ); |
694 | |
695 | /* but this should... */ |
696 | g_application_set_resource_base_path (application: app, resource_path: "/x" ); |
697 | g_assert_cmpstr (g_application_get_resource_base_path (app), ==, "/x" ); |
698 | |
699 | /* ... and this */ |
700 | g_application_set_resource_base_path (application: app, NULL); |
701 | g_assert_cmpstr (g_application_get_resource_base_path (app), ==, NULL); |
702 | |
703 | g_object_unref (object: app); |
704 | |
705 | /* Make sure that overriding at construction time works properly */ |
706 | app = g_object_new (G_TYPE_APPLICATION, first_property_name: "application-id" , "x.y.z" , "resource-base-path" , "/a" , NULL); |
707 | g_assert_cmpstr (g_application_get_resource_base_path (app), ==, "/a" ); |
708 | g_object_unref (object: app); |
709 | |
710 | /* ... particularly if we override to NULL */ |
711 | app = g_object_new (G_TYPE_APPLICATION, first_property_name: "application-id" , "x.y.z" , "resource-base-path" , NULL, NULL); |
712 | g_assert_cmpstr (g_application_get_resource_base_path (app), ==, NULL); |
713 | g_object_unref (object: app); |
714 | } |
715 | |
716 | static gint |
717 | test_help_command_line (GApplication *app, |
718 | GApplicationCommandLine *command_line, |
719 | gpointer user_data) |
720 | { |
721 | gboolean *called = user_data; |
722 | |
723 | *called = TRUE; |
724 | |
725 | return 0; |
726 | } |
727 | |
728 | /* Test whether --help is handled when HANDLES_COMMND_LINE is set and |
729 | * options have been added. |
730 | */ |
731 | static void |
732 | test_help (void) |
733 | { |
734 | if (g_test_subprocess ()) |
735 | { |
736 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
737 | gchar *argv[] = { binpath, "--help" , NULL }; |
738 | GApplication *app; |
739 | gboolean called = FALSE; |
740 | int status; |
741 | |
742 | app = g_application_new (application_id: "org.gtk.TestApplication" , flags: G_APPLICATION_HANDLES_COMMAND_LINE); |
743 | g_application_add_main_option (application: app, long_name: "foo" , short_name: 'f', flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
744 | g_signal_connect (app, "command-line" , G_CALLBACK (test_help_command_line), &called); |
745 | |
746 | status = g_application_run (application: app, G_N_ELEMENTS (argv) -1, argv); |
747 | g_assert (called == TRUE); |
748 | g_assert_cmpint (status, ==, 0); |
749 | |
750 | g_object_unref (object: app); |
751 | g_free (mem: binpath); |
752 | return; |
753 | } |
754 | |
755 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0); |
756 | g_test_trap_assert_passed (); |
757 | g_test_trap_assert_stdout ("*Application options*" ); |
758 | } |
759 | |
760 | static void |
761 | test_busy (void) |
762 | { |
763 | GApplication *app; |
764 | |
765 | /* use GSimpleAction to bind to the busy state, because it's easy to |
766 | * create and has an easily modifiable boolean property */ |
767 | GSimpleAction *action1; |
768 | GSimpleAction *action2; |
769 | |
770 | session_bus_up (); |
771 | |
772 | app = g_application_new (application_id: "org.gtk.TestApplication" , flags: G_APPLICATION_NON_UNIQUE); |
773 | g_assert (g_application_register (app, NULL, NULL)); |
774 | |
775 | g_assert (!g_application_get_is_busy (app)); |
776 | g_application_mark_busy (application: app); |
777 | g_assert (g_application_get_is_busy (app)); |
778 | g_application_unmark_busy (application: app); |
779 | g_assert (!g_application_get_is_busy (app)); |
780 | |
781 | action1 = g_simple_action_new (name: "action" , NULL); |
782 | g_application_bind_busy_property (application: app, object: action1, property: "enabled" ); |
783 | g_assert (g_application_get_is_busy (app)); |
784 | |
785 | g_simple_action_set_enabled (simple: action1, FALSE); |
786 | g_assert (!g_application_get_is_busy (app)); |
787 | |
788 | g_application_mark_busy (application: app); |
789 | g_assert (g_application_get_is_busy (app)); |
790 | |
791 | action2 = g_simple_action_new (name: "action" , NULL); |
792 | g_application_bind_busy_property (application: app, object: action2, property: "enabled" ); |
793 | g_assert (g_application_get_is_busy (app)); |
794 | |
795 | g_application_unmark_busy (application: app); |
796 | g_assert (g_application_get_is_busy (app)); |
797 | |
798 | g_object_unref (object: action2); |
799 | g_assert (!g_application_get_is_busy (app)); |
800 | |
801 | g_simple_action_set_enabled (simple: action1, TRUE); |
802 | g_assert (g_application_get_is_busy (app)); |
803 | |
804 | g_application_mark_busy (application: app); |
805 | g_assert (g_application_get_is_busy (app)); |
806 | |
807 | g_application_unbind_busy_property (application: app, object: action1, property: "enabled" ); |
808 | g_assert (g_application_get_is_busy (app)); |
809 | |
810 | g_application_unmark_busy (application: app); |
811 | g_assert (!g_application_get_is_busy (app)); |
812 | |
813 | g_object_unref (object: action1); |
814 | g_object_unref (object: app); |
815 | |
816 | session_bus_down (); |
817 | } |
818 | |
819 | /* |
820 | * Test that handle-local-options works as expected |
821 | */ |
822 | |
823 | static gint |
824 | test_local_options (GApplication *app, |
825 | GVariantDict *options, |
826 | gpointer data) |
827 | { |
828 | gboolean *called = data; |
829 | |
830 | *called = TRUE; |
831 | |
832 | if (g_variant_dict_contains (dict: options, key: "success" )) |
833 | return 0; |
834 | else if (g_variant_dict_contains (dict: options, key: "failure" )) |
835 | return 1; |
836 | else |
837 | return -1; |
838 | } |
839 | |
840 | static gint |
841 | second_handler (GApplication *app, |
842 | GVariantDict *options, |
843 | gpointer data) |
844 | { |
845 | gboolean *called = data; |
846 | |
847 | *called = TRUE; |
848 | |
849 | return 2; |
850 | } |
851 | |
852 | static void |
853 | test_handle_local_options_success (void) |
854 | { |
855 | if (g_test_subprocess ()) |
856 | { |
857 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
858 | gchar *argv[] = { binpath, "--success" , NULL }; |
859 | GApplication *app; |
860 | gboolean called = FALSE; |
861 | gboolean called2 = FALSE; |
862 | int status; |
863 | |
864 | app = g_application_new (application_id: "org.gtk.TestApplication" , flags: 0); |
865 | g_application_add_main_option (application: app, long_name: "success" , short_name: 0, flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
866 | g_application_add_main_option (application: app, long_name: "failure" , short_name: 0, flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
867 | g_signal_connect (app, "handle-local-options" , G_CALLBACK (test_local_options), &called); |
868 | g_signal_connect (app, "handle-local-options" , G_CALLBACK (second_handler), &called2); |
869 | |
870 | status = g_application_run (application: app, G_N_ELEMENTS (argv) -1, argv); |
871 | g_assert (called); |
872 | g_assert (!called2); |
873 | g_assert_cmpint (status, ==, 0); |
874 | |
875 | g_object_unref (object: app); |
876 | g_free (mem: binpath); |
877 | return; |
878 | } |
879 | |
880 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR); |
881 | g_test_trap_assert_passed (); |
882 | } |
883 | |
884 | static void |
885 | test_handle_local_options_failure (void) |
886 | { |
887 | if (g_test_subprocess ()) |
888 | { |
889 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
890 | gchar *argv[] = { binpath, "--failure" , NULL }; |
891 | GApplication *app; |
892 | gboolean called = FALSE; |
893 | gboolean called2 = FALSE; |
894 | int status; |
895 | |
896 | app = g_application_new (application_id: "org.gtk.TestApplication" , flags: 0); |
897 | g_application_add_main_option (application: app, long_name: "success" , short_name: 0, flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
898 | g_application_add_main_option (application: app, long_name: "failure" , short_name: 0, flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
899 | g_signal_connect (app, "handle-local-options" , G_CALLBACK (test_local_options), &called); |
900 | g_signal_connect (app, "handle-local-options" , G_CALLBACK (second_handler), &called2); |
901 | |
902 | status = g_application_run (application: app, G_N_ELEMENTS (argv) -1, argv); |
903 | g_assert (called); |
904 | g_assert (!called2); |
905 | g_assert_cmpint (status, ==, 1); |
906 | |
907 | g_object_unref (object: app); |
908 | g_free (mem: binpath); |
909 | return; |
910 | } |
911 | |
912 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR); |
913 | g_test_trap_assert_passed (); |
914 | } |
915 | |
916 | static void |
917 | test_handle_local_options_passthrough (void) |
918 | { |
919 | if (g_test_subprocess ()) |
920 | { |
921 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
922 | gchar *argv[] = { binpath, NULL }; |
923 | GApplication *app; |
924 | gboolean called = FALSE; |
925 | gboolean called2 = FALSE; |
926 | int status; |
927 | |
928 | app = g_application_new (application_id: "org.gtk.TestApplication" , flags: 0); |
929 | g_application_add_main_option (application: app, long_name: "success" , short_name: 0, flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
930 | g_application_add_main_option (application: app, long_name: "failure" , short_name: 0, flags: G_OPTION_FLAG_NONE, arg: G_OPTION_ARG_NONE, description: "" , arg_description: "" ); |
931 | g_signal_connect (app, "handle-local-options" , G_CALLBACK (test_local_options), &called); |
932 | g_signal_connect (app, "handle-local-options" , G_CALLBACK (second_handler), &called2); |
933 | |
934 | status = g_application_run (application: app, G_N_ELEMENTS (argv) -1, argv); |
935 | g_assert (called); |
936 | g_assert (called2); |
937 | g_assert_cmpint (status, ==, 2); |
938 | |
939 | g_object_unref (object: app); |
940 | g_free (mem: binpath); |
941 | return; |
942 | } |
943 | |
944 | g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR); |
945 | g_test_trap_assert_passed (); |
946 | } |
947 | |
948 | static void |
949 | test_api (void) |
950 | { |
951 | GApplication *app; |
952 | GSimpleAction *action; |
953 | |
954 | app = g_application_new (application_id: "org.gtk.TestApplication" , flags: 0); |
955 | |
956 | /* add an action without a name */ |
957 | g_test_expect_message (G_LOG_DOMAIN, log_level: G_LOG_LEVEL_CRITICAL, pattern: "*assertion*failed*" ); |
958 | action = g_simple_action_new (NULL, NULL); |
959 | g_assert (action == NULL); |
960 | g_test_assert_expected_messages (); |
961 | |
962 | /* also, gapplication shouldn't accept actions without names */ |
963 | action = g_object_new (G_TYPE_SIMPLE_ACTION, NULL); |
964 | g_test_expect_message (G_LOG_DOMAIN, log_level: G_LOG_LEVEL_CRITICAL, pattern: "*action has no name*" ); |
965 | g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (action)); |
966 | g_test_assert_expected_messages (); |
967 | |
968 | g_object_unref (object: action); |
969 | g_object_unref (object: app); |
970 | } |
971 | |
972 | /* Check that G_APPLICATION_ALLOW_REPLACEMENT works. To do so, we launch |
973 | * a GApplication in this process that allows replacement, and then |
974 | * launch a subprocess with --gapplication-replace. We have to do our |
975 | * own async version of g_test_trap_subprocess() here since we need |
976 | * the main process to keep spinning its mainloop. |
977 | */ |
978 | |
979 | static gboolean |
980 | name_was_lost (GApplication *app, |
981 | gboolean *called) |
982 | { |
983 | *called = TRUE; |
984 | g_application_quit (application: app); |
985 | return TRUE; |
986 | } |
987 | |
988 | static void |
989 | startup_in_subprocess (GApplication *app, |
990 | gboolean *called) |
991 | { |
992 | *called = TRUE; |
993 | } |
994 | |
995 | typedef struct |
996 | { |
997 | gboolean allow_replacement; |
998 | GSubprocess *subprocess; |
999 | } TestReplaceData; |
1000 | |
1001 | static void |
1002 | startup_cb (GApplication *app, |
1003 | TestReplaceData *data) |
1004 | { |
1005 | const char *argv[] = { NULL, "--verbose" , "--quiet" , "-p" , NULL, "--GTestSubprocess" , NULL }; |
1006 | GSubprocessLauncher *launcher; |
1007 | GError *local_error = NULL; |
1008 | |
1009 | g_application_hold (application: app); |
1010 | |
1011 | argv[0] = g_get_prgname (); |
1012 | |
1013 | if (data->allow_replacement) |
1014 | argv[4] = "/gapplication/replace" ; |
1015 | else |
1016 | argv[4] = "/gapplication/no-replace" ; |
1017 | |
1018 | /* Now that we are the primary instance, launch our replacement. |
1019 | * We inherit the environment to share the test session bus. |
1020 | */ |
1021 | g_test_message (format: "launching subprocess" ); |
1022 | |
1023 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_NONE); |
1024 | g_subprocess_launcher_set_environ (self: launcher, NULL); |
1025 | data->subprocess = g_subprocess_launcher_spawnv (self: launcher, argv, error: &local_error); |
1026 | g_assert_no_error (local_error); |
1027 | g_object_unref (object: launcher); |
1028 | |
1029 | if (!data->allow_replacement) |
1030 | { |
1031 | /* make sure we exit after a bit, if the subprocess is not replacing us */ |
1032 | g_application_set_inactivity_timeout (application: app, inactivity_timeout: 500); |
1033 | g_application_release (application: app); |
1034 | } |
1035 | } |
1036 | |
1037 | static void |
1038 | activate (gpointer data) |
1039 | { |
1040 | /* GApplication complains if we don't connect to ::activate */ |
1041 | } |
1042 | |
1043 | static gboolean |
1044 | quit_already (gpointer data) |
1045 | { |
1046 | GApplication *app = data; |
1047 | |
1048 | g_application_quit (application: app); |
1049 | |
1050 | return G_SOURCE_REMOVE; |
1051 | } |
1052 | |
1053 | static void |
1054 | test_replace (gconstpointer data) |
1055 | { |
1056 | gboolean allow = GPOINTER_TO_INT (data); |
1057 | |
1058 | if (g_test_subprocess ()) |
1059 | { |
1060 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
1061 | char *argv[] = { binpath, "--gapplication-replace" , NULL }; |
1062 | GApplication *app; |
1063 | gboolean startup = FALSE; |
1064 | |
1065 | app = g_application_new (application_id: "org.gtk.TestApplication.Replace" , flags: G_APPLICATION_ALLOW_REPLACEMENT); |
1066 | g_signal_connect (app, "startup" , G_CALLBACK (startup_in_subprocess), &startup); |
1067 | g_signal_connect (app, "activate" , G_CALLBACK (activate), NULL); |
1068 | |
1069 | g_application_run (application: app, G_N_ELEMENTS (argv) - 1, argv); |
1070 | |
1071 | if (allow) |
1072 | g_assert_true (startup); |
1073 | else |
1074 | g_assert_false (startup); |
1075 | |
1076 | g_object_unref (object: app); |
1077 | g_free (mem: binpath); |
1078 | } |
1079 | else |
1080 | { |
1081 | char *binpath = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "unimportant" , NULL); |
1082 | gchar *argv[] = { binpath, NULL }; |
1083 | GApplication *app; |
1084 | gboolean name_lost = FALSE; |
1085 | TestReplaceData data; |
1086 | GTestDBus *bus; |
1087 | |
1088 | data.allow_replacement = allow; |
1089 | data.subprocess = NULL; |
1090 | |
1091 | bus = g_test_dbus_new (flags: 0); |
1092 | g_test_dbus_up (self: bus); |
1093 | |
1094 | app = g_application_new (application_id: "org.gtk.TestApplication.Replace" , flags: allow ? G_APPLICATION_ALLOW_REPLACEMENT : G_APPLICATION_FLAGS_NONE); |
1095 | g_application_set_inactivity_timeout (application: app, inactivity_timeout: 500); |
1096 | g_signal_connect (app, "name-lost" , G_CALLBACK (name_was_lost), &name_lost); |
1097 | g_signal_connect (app, "startup" , G_CALLBACK (startup_cb), &data); |
1098 | g_signal_connect (app, "activate" , G_CALLBACK (activate), NULL); |
1099 | |
1100 | if (!allow) |
1101 | g_timeout_add_seconds (interval: 1, function: quit_already, data: app); |
1102 | |
1103 | g_application_run (application: app, G_N_ELEMENTS (argv) - 1, argv); |
1104 | |
1105 | g_assert_nonnull (data.subprocess); |
1106 | if (allow) |
1107 | g_assert_true (name_lost); |
1108 | else |
1109 | g_assert_false (name_lost); |
1110 | |
1111 | g_object_unref (object: app); |
1112 | g_free (mem: binpath); |
1113 | |
1114 | g_subprocess_wait (subprocess: data.subprocess, NULL, NULL); |
1115 | g_clear_object (&data.subprocess); |
1116 | |
1117 | g_test_dbus_down (self: bus); |
1118 | g_object_unref (object: bus); |
1119 | } |
1120 | } |
1121 | |
1122 | int |
1123 | main (int argc, char **argv) |
1124 | { |
1125 | g_setenv (variable: "LC_ALL" , value: "C" , TRUE); |
1126 | |
1127 | g_test_init (argc: &argc, argv: &argv, NULL); |
1128 | |
1129 | if (!g_test_subprocess ()) |
1130 | g_test_dbus_unset (); |
1131 | |
1132 | g_test_add_func (testpath: "/gapplication/no-dbus" , test_func: test_nodbus); |
1133 | /* g_test_add_func ("/gapplication/basic", basic); */ |
1134 | g_test_add_func (testpath: "/gapplication/no-appid" , test_func: test_noappid); |
1135 | /* g_test_add_func ("/gapplication/non-unique", test_nonunique); */ |
1136 | g_test_add_func (testpath: "/gapplication/properties" , test_func: properties); |
1137 | g_test_add_func (testpath: "/gapplication/app-id" , test_func: appid); |
1138 | g_test_add_func (testpath: "/gapplication/quit" , test_func: test_quit); |
1139 | g_test_add_func (testpath: "/gapplication/local-actions" , test_func: test_local_actions); |
1140 | /* g_test_add_func ("/gapplication/remote-actions", test_remote_actions); */ |
1141 | g_test_add_func (testpath: "/gapplication/local-command-line" , test_func: test_local_command_line); |
1142 | /* g_test_add_func ("/gapplication/remote-command-line", test_remote_command_line); */ |
1143 | g_test_add_func (testpath: "/gapplication/resource-path" , test_func: test_resource_path); |
1144 | g_test_add_func (testpath: "/gapplication/test-help" , test_func: test_help); |
1145 | g_test_add_func (testpath: "/gapplication/test-busy" , test_func: test_busy); |
1146 | g_test_add_func (testpath: "/gapplication/test-handle-local-options1" , test_func: test_handle_local_options_success); |
1147 | g_test_add_func (testpath: "/gapplication/test-handle-local-options2" , test_func: test_handle_local_options_failure); |
1148 | g_test_add_func (testpath: "/gapplication/test-handle-local-options3" , test_func: test_handle_local_options_passthrough); |
1149 | g_test_add_func (testpath: "/gapplication/api" , test_func: test_api); |
1150 | g_test_add_data_func (testpath: "/gapplication/replace" , GINT_TO_POINTER (TRUE), test_func: test_replace); |
1151 | g_test_add_data_func (testpath: "/gapplication/no-replace" , GINT_TO_POINTER (FALSE), test_func: test_replace); |
1152 | |
1153 | return g_test_run (); |
1154 | } |
1155 | |