1 /*
2   Confirm that no warnings/criticals get generated by including
3   multiple add/removes in a match w/o waiting on the mainloop to iterate.
4 
5   Copyright 2013 Canonical Ltd.
6 
7   Original sample code by Drew Bliss <drewb@valvesoftware.com>
8   Modified for automatic testing by Charles Kerr <charles.kerr@canonical.com>
9  */
10 
11 #include <stdlib.h> /* exit() */
12 #include <gtk/gtk.h>
13 #include <libdbusmenu-glib/server.h>
14 #include <libdbusmenu-gtk/menu.h>
15 #include <libdbusmenu-gtk/parser.h>
16 
17 static GMainLoop * loop = NULL;
18 static GtkWidget * top_gtk = NULL;
19 static DbusmenuMenuitem * top_dbusmenu = NULL;
20 static DbusmenuServer * menuservice = NULL;
21 
22 static void
rebuild_menu(void)23 rebuild_menu (void)
24 {
25   GList * l;
26   GList * children;
27   int i;
28   const int n = 10;
29   static int count = 0;
30 
31   if (top_gtk == NULL)
32     {
33       top_gtk = gtk_menu_new ();
34       gtk_widget_show (top_gtk);
35       top_dbusmenu = dbusmenu_gtk_parse_menu_structure (top_gtk);
36       menuservice = dbusmenu_server_new ("/org/ayatana/NotificationItem/test/Menu");
37       dbusmenu_server_set_root (menuservice, top_dbusmenu);
38     }
39 
40   // remove all the previous children
41   children = gtk_container_get_children (GTK_CONTAINER(top_gtk));
42   for (l=children; l!=NULL; l=l->next)
43     gtk_widget_destroy (GTK_WIDGET (l->data));
44 
45   // add a handful of new children
46   for (i=0; i<n; ++i)
47     {
48       char buf[80];
49       GtkWidget * child;
50 
51       g_snprintf (buf, sizeof(buf), "Test item %d", ++count);
52       child = gtk_menu_item_new_with_label (buf);
53       gtk_menu_shell_append (GTK_MENU_SHELL(top_gtk), child);
54       gtk_widget_show (child);
55     }
56 }
57 
58 /*
59  * Periodically rebuild the menu.
60  *
61  * After we've looped a couple of times with only one rebuild,
62  * attempt to reproduce the bug Drew reported by rebuilding multiple
63  * times before returning control to the main loop.
64  *
65  * If we survive doing this a handful of times without encountering
66  * a g_warning or g_critical, pass the test.
67  */
68 static gint
on_timer(gpointer unused G_GNUC_UNUSED)69 on_timer (gpointer unused G_GNUC_UNUSED)
70 {
71   static int iteration = 0;
72 
73   ++iteration;
74 
75   if (iteration > 5)
76     {
77       g_main_loop_quit (loop);
78       return G_SOURCE_REMOVE;
79     }
80 
81   if (iteration <= 2)
82     {
83       rebuild_menu ();
84     }
85   else
86     {
87       int i;
88 
89       for (i=0; i<iteration; ++i)
90         rebuild_menu ();
91     }
92 
93   return G_SOURCE_CONTINUE;
94 }
95 
96 static void
warning_counter(const gchar * log_domain,GLogLevelFlags log_level G_GNUC_UNUSED,const gchar * message,gpointer user_data G_GNUC_UNUSED)97 warning_counter (const gchar    * log_domain,
98                  GLogLevelFlags   log_level G_GNUC_UNUSED,
99                  const gchar    * message,
100                  gpointer         user_data G_GNUC_UNUSED)
101 {
102   g_message ("Failing the test due to warning: %s %s", log_domain, message);
103   exit (EXIT_FAILURE);
104 }
105 
106 int
main(int argc,char ** argv)107 main (int argc, char ** argv)
108 {
109   g_log_set_handler ("LIBDBUSMENU-GLIB", G_LOG_LEVEL_WARNING|G_LOG_LEVEL_CRITICAL, warning_counter, NULL);
110 
111 
112   gtk_init (&argc, &argv);
113   loop = g_main_loop_new (NULL, FALSE);
114   g_timeout_add (200, on_timer, NULL);
115   g_main_loop_run (loop);
116 
117   return 0;
118 }
119