1 #define wake_up wake_up_void
2 #include "../src/dbus.c"
3 #include "greatest.h"
4 
5 #include <assert.h>
6 #include <gdk-pixbuf/gdk-pixbuf.h>
7 #include <gio/gio.h>
8 
9 #include "helpers.h"
10 #include "queues.h"
11 
12 extern const char *base;
13 
wake_up_void(void)14 void wake_up_void(void) {  }
15 
16 struct signal_actioninvoked {
17         guint id;
18         gchar *key;
19         guint subscription_id;
20         GDBusConnection *conn;
21 };
22 
23 struct signal_closed {
24         guint32 id;
25         guint32 reason;
26         guint subscription_id;
27         GDBusConnection *conn;
28 };
29 
dbus_signal_cb_actioninvoked(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)30 void dbus_signal_cb_actioninvoked(GDBusConnection *connection,
31                                   const gchar *sender_name,
32                                   const gchar *object_path,
33                                   const gchar *interface_name,
34                                   const gchar *signal_name,
35                                   GVariant *parameters,
36                                   gpointer user_data)
37 {
38         g_return_if_fail(user_data);
39 
40         guint32 id;
41         gchar *key;
42 
43         struct signal_actioninvoked *sig = (struct signal_actioninvoked*) user_data;
44 
45         g_variant_get(parameters, "(us)", &id, &key);
46 
47         if (id == sig->id) {
48                 sig->id = id;
49                 sig->key = key;
50         }
51 }
52 
dbus_signal_subscribe_actioninvoked(struct signal_actioninvoked * actioninvoked)53 void dbus_signal_subscribe_actioninvoked(struct signal_actioninvoked *actioninvoked)
54 {
55         assert(actioninvoked);
56 
57         actioninvoked->conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
58         actioninvoked->subscription_id =
59                 g_dbus_connection_signal_subscribe(
60                         actioninvoked->conn,
61                         FDN_NAME,
62                         FDN_IFAC,
63                         "ActionInvoked",
64                         FDN_PATH,
65                         NULL,
66                         G_DBUS_SIGNAL_FLAGS_NONE,
67                         dbus_signal_cb_actioninvoked,
68                         actioninvoked,
69                         NULL);
70 }
71 
dbus_signal_unsubscribe_actioninvoked(struct signal_actioninvoked * actioninvoked)72 void dbus_signal_unsubscribe_actioninvoked(struct signal_actioninvoked *actioninvoked)
73 {
74         assert(actioninvoked);
75 
76         g_dbus_connection_signal_unsubscribe(actioninvoked->conn, actioninvoked->subscription_id);
77         g_object_unref(actioninvoked->conn);
78 
79         actioninvoked->conn = NULL;
80         actioninvoked->subscription_id = -1;
81 }
82 
dbus_signal_cb_closed(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)83 void dbus_signal_cb_closed(GDBusConnection *connection,
84                  const gchar *sender_name,
85                  const gchar *object_path,
86                  const gchar *interface_name,
87                  const gchar *signal_name,
88                  GVariant *parameters,
89                  gpointer user_data)
90 {
91         g_return_if_fail(user_data);
92 
93         guint32 id;
94         guint32 reason;
95 
96         struct signal_closed *sig = (struct signal_closed*) user_data;
97         g_variant_get(parameters, "(uu)", &id, &reason);
98 
99         if (id == sig->id) {
100                 sig->id = id;
101                 sig->reason = reason;
102         }
103 }
104 
dbus_signal_subscribe_closed(struct signal_closed * closed)105 void dbus_signal_subscribe_closed(struct signal_closed *closed)
106 {
107         assert(closed);
108 
109         closed->conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
110         closed->subscription_id =
111                 g_dbus_connection_signal_subscribe(
112                         closed->conn,
113                         FDN_NAME,
114                         FDN_IFAC,
115                         "NotificationClosed",
116                         FDN_PATH,
117                         NULL,
118                         G_DBUS_SIGNAL_FLAGS_NONE,
119                         dbus_signal_cb_closed,
120                         closed,
121                         NULL);
122 }
123 
dbus_signal_unsubscribe_closed(struct signal_closed * closed)124 void dbus_signal_unsubscribe_closed(struct signal_closed *closed)
125 {
126         assert(closed);
127 
128         g_dbus_connection_signal_unsubscribe(closed->conn, closed->subscription_id);
129         g_object_unref(closed->conn);
130 
131         closed->conn = NULL;
132         closed->subscription_id = -1;
133 }
134 
dbus_invoke(const char * method,GVariant * params)135 GVariant *dbus_invoke(const char *method, GVariant *params)
136 {
137         GDBusConnection *connection_client;
138         GVariant *retdata;
139         GError *error = NULL;
140 
141         connection_client = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
142         retdata = g_dbus_connection_call_sync(
143                                 connection_client,
144                                 FDN_NAME,
145                                 FDN_PATH,
146                                 FDN_IFAC,
147                                 method,
148                                 params,
149                                 NULL,
150                                 G_DBUS_CALL_FLAGS_NONE,
151                                 -1,
152                                 NULL,
153                                 &error);
154         if (error) {
155                 printf("Error while calling GTestDBus instance: %s\n", error->message);
156                 g_error_free(error);
157         }
158 
159         g_object_unref(connection_client);
160 
161         return retdata;
162 }
163 
164 struct dbus_notification {
165         const char* app_name;
166         guint replaces_id;
167         const char* app_icon;
168         const char* summary;
169         const char* body;
170         GHashTable *actions;
171         GHashTable *hints;
172         int expire_timeout;
173 };
174 
g_free_variant_value(gpointer tofree)175 void g_free_variant_value(gpointer tofree)
176 {
177         g_variant_unref((GVariant*) tofree);
178 }
179 
dbus_notification_new(void)180 struct dbus_notification *dbus_notification_new(void)
181 {
182         struct dbus_notification *n = g_malloc0(sizeof(struct dbus_notification));
183         n->expire_timeout = -1;
184         n->replaces_id = 0;
185         n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
186         n->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_variant_value);
187         return n;
188 }
189 
dbus_notification_free(struct dbus_notification * n)190 void dbus_notification_free(struct dbus_notification *n)
191 {
192         g_hash_table_unref(n->hints);
193         g_hash_table_unref(n->actions);
194         g_free(n);
195 }
196 
dbus_notification_fire(struct dbus_notification * n,uint * id)197 bool dbus_notification_fire(struct dbus_notification *n, uint *id)
198 {
199         assert(n);
200         assert(id);
201         GVariantBuilder b;
202         GVariantType *t;
203 
204         gpointer p_key;
205         gpointer p_value;
206         GHashTableIter iter;
207 
208         t = g_variant_type_new("(susssasa{sv}i)");
209         g_variant_builder_init(&b, t);
210         g_variant_type_free(t);
211 
212         g_variant_builder_add(&b, "s", n->app_name);
213         g_variant_builder_add(&b, "u", n->replaces_id);
214         g_variant_builder_add(&b, "s", n->app_icon);
215         g_variant_builder_add(&b, "s", n->summary);
216         g_variant_builder_add(&b, "s", n->body);
217 
218         // Add the actions
219         t = g_variant_type_new("as");
220         g_variant_builder_open(&b, t);
221         g_hash_table_iter_init(&iter, n->actions);
222         while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
223                 g_variant_builder_add(&b, "s", (char*)p_key);
224                 g_variant_builder_add(&b, "s", (char*)p_value);
225         }
226         // Add an invalid appendix to cover odd numbered action arrays
227         // Shouldn't interfere with normal testing
228         g_variant_builder_add(&b, "s", "invalid appendix");
229         g_variant_builder_close(&b);
230         g_variant_type_free(t);
231 
232         // Add the hints
233         t = g_variant_type_new("a{sv}");
234         g_variant_builder_open(&b, t);
235         g_hash_table_iter_init(&iter, n->hints);
236         while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
237                 g_variant_builder_add(&b, "{sv}", (char*)p_key, (GVariant*)p_value);
238         }
239         g_variant_builder_close(&b);
240         g_variant_type_free(t);
241 
242         g_variant_builder_add(&b, "i", n->expire_timeout);
243 
244         GVariant *reply = dbus_invoke("Notify", g_variant_builder_end(&b));
245         if (reply) {
246                 g_variant_get(reply, "(u)", id);
247                 g_variant_unref(reply);
248                 return true;
249         } else {
250                 return false;
251         }
252 }
253 
dbus_notification_set_raw_image(struct dbus_notification * n_dbus,const char * path)254 void dbus_notification_set_raw_image(struct dbus_notification *n_dbus, const char *path)
255 {
256         GVariant *hint = notification_setup_raw_image(path);
257         if (!hint)
258                 return;
259 
260         g_hash_table_insert(n_dbus->hints,
261                             g_strdup("image-data"),
262                             g_variant_ref_sink(hint));
263 }
264 
265 /////// TESTS
266 gint owner_id;
267 
test_dbus_init(void)268 TEST test_dbus_init(void)
269 {
270         owner_id = dbus_init();
271         uint waiting = 0;
272         while (!dbus_conn && waiting < 2000) {
273                 usleep(500);
274                 waiting++;
275         }
276         ASSERTm("After 1s, there is still no dbus connection available.",
277                 dbus_conn);
278         PASS();
279 }
280 
test_dbus_teardown(void)281 TEST test_dbus_teardown(void)
282 {
283         dbus_teardown(owner_id);
284         PASS();
285 }
286 
test_invalid_notification(void)287 TEST test_invalid_notification(void)
288 {
289         GVariant *faulty = g_variant_new_boolean(true);
290 
291         ASSERT(NULL == dbus_message_to_notification(":123", faulty));
292         ASSERT(NULL == dbus_invoke("Notify", faulty));
293 
294         g_variant_unref(faulty);
295         PASS();
296 }
297 
test_empty_notification(void)298 TEST test_empty_notification(void)
299 {
300         struct dbus_notification *n = dbus_notification_new();
301         gsize len = queues_length_waiting();
302 
303         guint id;
304         ASSERT(dbus_notification_fire(n, &id));
305         ASSERT(id != 0);
306 
307         ASSERT_EQ(queues_length_waiting(), len+1);
308         dbus_notification_free(n);
309         PASS();
310 }
311 
test_basic_notification(void)312 TEST test_basic_notification(void)
313 {
314         struct dbus_notification *n = dbus_notification_new();
315         gsize len = queues_length_waiting();
316         n->app_name = "dunstteststack";
317         n->app_icon = "NONE";
318         n->summary = "Headline";
319         n->body = "Text";
320         g_hash_table_insert(n->actions, g_strdup("actionid"), g_strdup("Print this text"));
321         g_hash_table_insert(n->hints,
322                             g_strdup("x-dunst-stack-tag"),
323                             g_variant_ref_sink(g_variant_new_string("volume")));
324 
325         n->replaces_id = 10;
326 
327         guint id;
328         ASSERT(dbus_notification_fire(n, &id));
329         ASSERT(id != 0);
330 
331         ASSERT_EQ(queues_length_waiting(), len+1);
332         dbus_notification_free(n);
333         PASS();
334 }
335 
test_dbus_notify_colors(void)336 TEST test_dbus_notify_colors(void)
337 {
338         const char *color_frame = "I allow all string values for frame!";
339         const char *color_bg = "I allow all string values for background!";
340         const char *color_fg = "I allow all string values for foreground!";
341         struct notification *n;
342         struct dbus_notification *n_dbus;
343 
344         gsize len = queues_length_waiting();
345 
346         n_dbus = dbus_notification_new();
347         n_dbus->app_name = "dunstteststack";
348         n_dbus->app_icon = "NONE";
349         n_dbus->summary = "test_dbus_notify_colors";
350         n_dbus->body = "Summary of it";
351         g_hash_table_insert(n_dbus->hints,
352                             g_strdup("frcolor"),
353                             g_variant_ref_sink(g_variant_new_string(color_frame)));
354         g_hash_table_insert(n_dbus->hints,
355                             g_strdup("bgcolor"),
356                             g_variant_ref_sink(g_variant_new_string(color_bg)));
357         g_hash_table_insert(n_dbus->hints,
358                             g_strdup("fgcolor"),
359                             g_variant_ref_sink(g_variant_new_string(color_fg)));
360 
361         guint id;
362         ASSERT(dbus_notification_fire(n_dbus, &id));
363         ASSERT(id != 0);
364 
365         ASSERT_EQ(queues_length_waiting(), len+1);
366 
367         n = queues_debug_find_notification_by_id(id);
368 
369         ASSERT_STR_EQ(n->colors.frame, color_frame);
370         ASSERT_STR_EQ(n->colors.fg, color_fg);
371         ASSERT_STR_EQ(n->colors.bg, color_bg);
372 
373         dbus_notification_free(n_dbus);
374 
375         PASS();
376 }
377 
test_hint_transient(void)378 TEST test_hint_transient(void)
379 {
380         static char msg[50];
381         struct notification *n;
382         struct dbus_notification *n_dbus;
383 
384         gsize len = queues_length_waiting();
385 
386         n_dbus = dbus_notification_new();
387         n_dbus->app_name = "dunstteststack";
388         n_dbus->app_icon = "NONE";
389         n_dbus->summary = "test_hint_transient";
390         n_dbus->body = "Summary of it";
391 
392         bool values[] = { true, true, true, false, false, false, false };
393         GVariant *variants[] = {
394                 g_variant_new_boolean(true),
395                 g_variant_new_int32(1),
396                 g_variant_new_uint32(1),
397                 g_variant_new_boolean(false),
398                 g_variant_new_int32(0),
399                 g_variant_new_uint32(0),
400                 g_variant_new_int32(-1),
401         };
402         for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
403                 g_hash_table_insert(n_dbus->hints,
404                                     g_strdup("transient"),
405                                     g_variant_ref_sink(variants[i]));
406 
407                 guint id;
408                 ASSERT(dbus_notification_fire(n_dbus, &id));
409                 ASSERT(id != 0);
410 
411                 ASSERT_EQ(queues_length_waiting(), len+1);
412 
413                 n = queues_debug_find_notification_by_id(id);
414 
415                 snprintf(msg, sizeof(msg), "In round %ld", i);
416                 ASSERT_EQm(msg, values[i], n->transient);
417         }
418 
419         dbus_notification_free(n_dbus);
420 
421         PASS();
422 }
423 
test_hint_progress(void)424 TEST test_hint_progress(void)
425 {
426         static char msg[50];
427         struct notification *n;
428         struct dbus_notification *n_dbus;
429 
430         gsize len = queues_length_waiting();
431 
432         n_dbus = dbus_notification_new();
433         n_dbus->app_name = "dunstteststack";
434         n_dbus->app_icon = "NONE";
435         n_dbus->summary = "test_hint_progress";
436         n_dbus->body = "Summary of it";
437 
438         int values[] = { 99, 12, 123, 123, -1, -1 };
439         GVariant *variants[] = {
440                 g_variant_new_int32(99),
441                 g_variant_new_uint32(12),
442                 g_variant_new_int32(123), // allow higher than 100
443                 g_variant_new_uint32(123),
444                 g_variant_new_int32(-192),
445                 g_variant_new_uint32(-192),
446         };
447         for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
448                 g_hash_table_insert(n_dbus->hints,
449                                     g_strdup("value"),
450                                     g_variant_ref_sink(variants[i]));
451 
452                 guint id;
453                 ASSERT(dbus_notification_fire(n_dbus, &id));
454                 ASSERT(id != 0);
455 
456                 ASSERT_EQ(queues_length_waiting(), len+1);
457 
458                 n = queues_debug_find_notification_by_id(id);
459 
460                 snprintf(msg, sizeof(msg), "In round %ld", i);
461                 ASSERT_EQm(msg, values[i], n->progress);
462         }
463 
464         dbus_notification_free(n_dbus);
465 
466         PASS();
467 }
468 
test_hint_icons(void)469 TEST test_hint_icons(void)
470 {
471         struct notification *n;
472         struct dbus_notification *n_dbus;
473         const char *iconname = "NEWICON";
474 
475         gsize len = queues_length_waiting();
476 
477         n_dbus = dbus_notification_new();
478         n_dbus->app_name = "dunstteststack";
479         n_dbus->app_icon = "NONE";
480         n_dbus->summary = "test_hint_icons";
481         n_dbus->body = "Summary of it";
482 
483         g_hash_table_insert(n_dbus->hints,
484                             g_strdup("image-path"),
485                             g_variant_ref_sink(g_variant_new_string(iconname)));
486 
487         guint id;
488         ASSERT(dbus_notification_fire(n_dbus, &id));
489         ASSERT(id != 0);
490 
491         ASSERT_EQ(queues_length_waiting(), len+1);
492 
493         n = queues_debug_find_notification_by_id(id);
494 
495         ASSERT_STR_EQ(iconname, n->iconname);
496 
497         dbus_notification_free(n_dbus);
498 
499         PASS();
500 }
501 
test_hint_category(void)502 TEST test_hint_category(void)
503 {
504         struct notification *n;
505         struct dbus_notification *n_dbus;
506         const char *category = "VOLUME";
507 
508         gsize len = queues_length_waiting();
509 
510         n_dbus = dbus_notification_new();
511         n_dbus->app_name = "dunstteststack";
512         n_dbus->app_icon = "NONE";
513         n_dbus->summary = "test_hint_category";
514         n_dbus->body = "Summary of it";
515 
516         g_hash_table_insert(n_dbus->hints,
517                             g_strdup("category"),
518                             g_variant_ref_sink(g_variant_new_string(category)));
519 
520         guint id;
521         ASSERT(dbus_notification_fire(n_dbus, &id));
522         ASSERT(id != 0);
523 
524         ASSERT_EQ(queues_length_waiting(), len+1);
525 
526         n = queues_debug_find_notification_by_id(id);
527 
528         ASSERT_STR_EQ(category, n->category);
529 
530         dbus_notification_free(n_dbus);
531 
532         PASS();
533 }
534 
test_hint_desktop_entry(void)535 TEST test_hint_desktop_entry(void)
536 {
537         struct notification *n;
538         struct dbus_notification *n_dbus;
539         const char *desktop_entry = "org.dunst-project.dunst";
540 
541         gsize len = queues_length_waiting();
542 
543         n_dbus = dbus_notification_new();
544         n_dbus->app_name = "dunstteststack";
545         n_dbus->app_icon = "NONE";
546         n_dbus->summary = "test_hint_desktopentry";
547         n_dbus->body = "Summary of my desktop_entry";
548 
549         g_hash_table_insert(n_dbus->hints,
550                             g_strdup("desktop-entry"),
551                             g_variant_ref_sink(g_variant_new_string(desktop_entry)));
552 
553         guint id;
554         ASSERT(dbus_notification_fire(n_dbus, &id));
555         ASSERT(id != 0);
556 
557         ASSERT_EQ(queues_length_waiting(), len+1);
558 
559         n = queues_debug_find_notification_by_id(id);
560 
561         ASSERT_STR_EQ(desktop_entry, n->desktop_entry);
562 
563         dbus_notification_free(n_dbus);
564 
565         PASS();
566 }
567 
test_hint_urgency(void)568 TEST test_hint_urgency(void)
569 {
570         static char msg[50];
571         struct notification *n;
572         struct dbus_notification *n_dbus;
573 
574         gsize len = queues_length_waiting();
575 
576         n_dbus = dbus_notification_new();
577         n_dbus->app_name = "dunstteststack";
578         n_dbus->app_icon = "NONE";
579         n_dbus->summary = "test_hint_urgency";
580         n_dbus->body = "Summary of it";
581 
582         enum urgency values[] = { URG_MAX, URG_LOW, URG_NORM, URG_CRIT };
583         GVariant *variants[] = {
584                 g_variant_new_byte(10),
585                 g_variant_new_byte(0),
586                 g_variant_new_byte(1),
587                 g_variant_new_byte(2),
588         };
589         for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
590                 g_hash_table_insert(n_dbus->hints,
591                                     g_strdup("urgency"),
592                                     g_variant_ref_sink(variants[i]));
593 
594                 guint id;
595                 ASSERT(dbus_notification_fire(n_dbus, &id));
596                 ASSERT(id != 0);
597 
598                 ASSERT_EQ(queues_length_waiting(), len+1);
599 
600                 n = queues_debug_find_notification_by_id(id);
601 
602                 snprintf(msg, sizeof(msg), "In round %ld", i);
603                 ASSERT_EQm(msg, values[i], n->urgency);
604 
605                 queues_notification_close_id(id, REASON_UNDEF);
606         }
607 
608         dbus_notification_free(n_dbus);
609 
610         PASS();
611 }
612 
test_hint_raw_image(void)613 TEST test_hint_raw_image(void)
614 {
615         guint id;
616         struct notification *n;
617         struct dbus_notification *n_dbus;
618 
619         char *path = g_strconcat(base, "/data/icons/valid.png", NULL);
620         gsize len = queues_length_waiting();
621 
622         n_dbus = dbus_notification_new();
623         dbus_notification_set_raw_image(n_dbus, path);
624         n_dbus->app_name = "dunstteststack";
625         n_dbus->app_icon = "NONE";
626         n_dbus->summary = "test_hint_raw_image";
627         n_dbus->body = "Summary of it";
628 
629         ASSERT(dbus_notification_fire(n_dbus, &id));
630         ASSERT(id != 0);
631 
632         ASSERT_EQ(queues_length_waiting(), len+1);
633         n = queues_debug_find_notification_by_id(id);
634 
635         ASSERT(n->icon);
636         ASSERT(!STR_EQ(n->icon_id, n_dbus->app_icon));
637 
638         dbus_notification_free(n_dbus);
639         g_free(path);
640 
641         PASS();
642 }
643 
644 /* We didn't process the timeout parameter via DBus correctly
645  * and it got limited to an int instead of a long int
646  * See: Issue #646 (The timeout value in dunst wraps around) */
test_timeout_overflow(void)647 TEST test_timeout_overflow(void)
648 {
649         struct notification *n;
650         struct dbus_notification *n_dbus;
651 
652         n_dbus = dbus_notification_new();
653         n_dbus->app_name = "dunstteststack";
654         n_dbus->app_icon = "NONE";
655         n_dbus->summary = "test_hint_urgency";
656         n_dbus->body = "Summary of it";
657         n_dbus->expire_timeout = 2147484;
658         gint64 expected_timeout = G_GINT64_CONSTANT(2147484000);
659 
660         guint id;
661         ASSERT(dbus_notification_fire(n_dbus, &id));
662         ASSERT(id != 0);
663 
664         n = queues_debug_find_notification_by_id(id);
665         ASSERT_EQ_FMT(expected_timeout, n->timeout, "%" G_GINT64_FORMAT);
666 
667         dbus_notification_free(n_dbus);
668 
669         PASS();
670 }
671 
test_server_caps(enum markup_mode markup)672 TEST test_server_caps(enum markup_mode markup)
673 {
674         GVariant *reply;
675         GVariant *caps = NULL;
676         const char **capsarray;
677 
678         settings.markup = markup;
679 
680         reply = dbus_invoke("GetCapabilities", NULL);
681 
682         caps = g_variant_get_child_value(reply, 0);
683         capsarray = g_variant_get_strv(caps, NULL);
684 
685         ASSERT(capsarray);
686         ASSERT(g_strv_contains(capsarray, "actions"));
687         ASSERT(g_strv_contains(capsarray, "body"));
688         ASSERT(g_strv_contains(capsarray, "body-hyperlinks"));
689         ASSERT(g_strv_contains(capsarray, "x-dunst-stack-tag"));
690 
691         if (settings.markup != MARKUP_NO)
692                 ASSERT(g_strv_contains(capsarray, "body-markup"));
693         else
694                 ASSERT_FALSE(g_strv_contains(capsarray, "body-markup"));
695 
696         g_free(capsarray);
697         g_variant_unref(caps);
698         g_variant_unref(reply);
699         PASS();
700 }
701 
test_signal_actioninvoked(void)702 TEST test_signal_actioninvoked(void)
703 {
704         const struct notification *n;
705         struct dbus_notification *n_dbus;
706         struct signal_actioninvoked sig = {0, NULL, -1};
707 
708         dbus_signal_subscribe_actioninvoked(&sig);
709 
710         n_dbus = dbus_notification_new();
711         n_dbus->app_name = "dunstteststack";
712         n_dbus->app_icon = "NONE2";
713         n_dbus->summary = "Headline for New";
714         n_dbus->body = "Text";
715         g_hash_table_insert(n_dbus->actions, g_strdup("actionkey"), g_strdup("Print this text"));
716 
717         dbus_notification_fire(n_dbus, &sig.id);
718         n = queues_debug_find_notification_by_id(sig.id);
719 
720         signal_action_invoked(n, "actionkey");
721 
722         uint waiting = 0;
723         while (!sig.key && waiting < 2000) {
724                 usleep(500);
725                 waiting++;
726         }
727 
728         ASSERT_STR_EQ("actionkey", sig.key);
729 
730         g_free(sig.key);
731         dbus_notification_free(n_dbus);
732         dbus_signal_unsubscribe_actioninvoked(&sig);
733         PASS();
734 }
735 
test_close_and_signal(void)736 TEST test_close_and_signal(void)
737 {
738         GVariant *data, *ret;
739         struct dbus_notification *n;
740         struct signal_closed sig = {0, REASON_MIN-1, -1};
741 
742         dbus_signal_subscribe_closed(&sig);
743 
744         n = dbus_notification_new();
745         n->app_name = "dunstteststack";
746         n->app_icon = "NONE2";
747         n->summary = "Headline for New";
748         n->body = "Text";
749 
750         dbus_notification_fire(n, &sig.id);
751 
752         data = g_variant_new("(u)", sig.id);
753         ret = dbus_invoke("CloseNotification", data);
754 
755         ASSERT(ret);
756 
757         uint waiting = 0;
758         while (sig.reason == REASON_MIN-1 && waiting < 2000) {
759                 usleep(500);
760                 waiting++;
761         }
762 
763         ASSERT(sig.reason != REASON_MIN-1);
764 
765         dbus_notification_free(n);
766         dbus_signal_unsubscribe_closed(&sig);
767         g_variant_unref(ret);
768         PASS();
769 }
770 
test_get_fdn_daemon_info(void)771 TEST test_get_fdn_daemon_info(void)
772 {
773         unsigned int pid_is;
774         pid_t pid_should;
775         char *name, *vendor;
776         GDBusConnection *conn;
777 
778         pid_should = getpid();
779         conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
780 
781         ASSERT(dbus_get_fdn_daemon_info(conn, &pid_is, &name, &vendor));
782 
783         ASSERT_EQ_FMT(pid_should, pid_is, "%d");
784         ASSERT_STR_EQ("dunst", name);
785         ASSERT_STR_EQ("knopwob", vendor);
786 
787         g_free(name);
788         g_free(vendor);
789 
790         g_object_unref(conn);
791         PASS();
792 }
793 
assert_methodlists_sorted(void)794 TEST assert_methodlists_sorted(void)
795 {
796         for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_fdn); i++) {
797                 ASSERT(0 > strcmp(
798                                 methods_fdn[i].method_name,
799                                 methods_fdn[i+1].method_name));
800         }
801 
802         for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_dunst); i++) {
803                 ASSERT(0 > strcmp(
804                                 methods_dunst[i].method_name,
805                                 methods_dunst[i+1].method_name));
806         }
807 
808         PASS();
809 }
810 
811 
812 // TESTS END
813 
814 GMainLoop *loop;
815 GThread *thread_tests;
816 
run_threaded_tests(gpointer data)817 gpointer run_threaded_tests(gpointer data)
818 {
819         RUN_TEST(test_dbus_init);
820 
821         RUN_TEST(test_get_fdn_daemon_info);
822 
823         RUN_TEST(test_empty_notification);
824         RUN_TEST(test_basic_notification);
825         RUN_TEST(test_invalid_notification);
826         RUN_TEST(test_hint_transient);
827         RUN_TEST(test_hint_progress);
828         RUN_TEST(test_hint_icons);
829         RUN_TEST(test_hint_category);
830         RUN_TEST(test_hint_desktop_entry);
831         RUN_TEST(test_hint_urgency);
832         RUN_TEST(test_hint_raw_image);
833         RUN_TEST(test_dbus_notify_colors);
834         RUN_TESTp(test_server_caps, MARKUP_FULL);
835         RUN_TESTp(test_server_caps, MARKUP_STRIP);
836         RUN_TESTp(test_server_caps, MARKUP_NO);
837         RUN_TEST(test_close_and_signal);
838         RUN_TEST(test_signal_actioninvoked);
839         RUN_TEST(test_timeout_overflow);
840 
841         RUN_TEST(assert_methodlists_sorted);
842 
843         RUN_TEST(test_dbus_teardown);
844         g_main_loop_quit(loop);
845         return NULL;
846 }
847 
SUITE(suite_dbus)848 SUITE(suite_dbus)
849 {
850         settings.icon_path = "";
851 
852         GTestDBus *dbus_bus;
853         g_test_dbus_unset();
854         queues_init();
855 
856         loop = g_main_loop_new(NULL, false);
857 
858         dbus_bus = g_test_dbus_new(G_TEST_DBUS_NONE);
859         g_test_dbus_up(dbus_bus);
860 
861         thread_tests = g_thread_new("testexecutor", run_threaded_tests, loop);
862         g_main_loop_run(loop);
863 
864         queues_teardown();
865         g_test_dbus_down(dbus_bus);
866         g_object_unref(dbus_bus);
867         g_thread_unref(thread_tests);
868         g_main_loop_unref(loop);
869 
870         settings.icon_path = NULL;
871 }
872 
873 /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
874