1 /* objects-finalize.c
2  * Copyright (C) 2013 Openismus GmbH
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Tristan Van Berkom <tristanvb@openismus.com>
18  */
19 #include <gtk/gtk.h>
20 #include <string.h>
21 
22 #ifdef GDK_WINDOWING_X11
23 # include <gdk/x11/gdkx.h>
24 #endif
25 
26 
27 typedef GType (*GTypeGetFunc) (void);
28 
29 static gboolean finalized = FALSE;
30 
31 static gboolean
main_loop_quit_cb(gpointer data)32 main_loop_quit_cb (gpointer data)
33 {
34   gboolean *done = data;
35 
36   *done = TRUE;
37 
38   g_main_context_wakeup (NULL);
39 
40   g_assert_true (finalized);
41   return FALSE;
42 }
43 
44 static void
check_finalized(gpointer data,GObject * where_the_object_was)45 check_finalized (gpointer data,
46 		 GObject *where_the_object_was)
47 {
48   gboolean *did_finalize = (gboolean *)data;
49 
50   *did_finalize = TRUE;
51 }
52 
53 static void
test_finalize_object(gconstpointer data)54 test_finalize_object (gconstpointer data)
55 {
56   GType test_type = GPOINTER_TO_SIZE (data);
57   GObject *object;
58   gboolean done;
59 
60   if (g_str_equal (g_type_name (test_type), "GdkClipboard"))
61     object = g_object_new (test_type, "display", gdk_display_get_default (), NULL);
62   else if (g_str_equal (g_type_name (test_type), "GdkDrag") ||
63            g_str_equal (g_type_name (test_type), "GdkDrop"))
64     {
65       GdkContentFormats *formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
66       object = g_object_new (test_type,
67                              "device", gdk_seat_get_pointer (gdk_display_get_default_seat (gdk_display_get_default ())),
68                              "formats", formats,
69                              NULL);
70       gdk_content_formats_unref (formats);
71     }
72   else if (g_type_is_a (test_type, GSK_TYPE_GL_SHADER))
73     {
74       GBytes *bytes = g_bytes_new_static ("", 0);
75       object = g_object_new (test_type, "source", bytes, NULL);
76       g_bytes_unref (bytes);
77     }
78   else if (g_type_is_a (test_type, GTK_TYPE_FILTER_LIST_MODEL) ||
79            g_type_is_a (test_type, GTK_TYPE_NO_SELECTION) ||
80            g_type_is_a (test_type, GTK_TYPE_SINGLE_SELECTION) ||
81            g_type_is_a (test_type, GTK_TYPE_MULTI_SELECTION))
82     {
83       GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
84       object = g_object_new (test_type,
85                              "model", list_store,
86                              NULL);
87       g_object_unref (list_store);
88     }
89   else if (g_type_is_a (test_type, GTK_TYPE_LAYOUT_CHILD))
90     {
91 #if 0
92       // See https://github.com/mesonbuild/meson/issues/7515
93       char *msg = g_strdup_printf ("Skipping %s", g_type_name (test_type));
94       g_test_skip (msg);
95       g_free (msg);
96 #endif
97       return;
98     }
99   else
100     object = g_object_new (test_type, NULL);
101   g_assert_true (G_IS_OBJECT (object));
102 
103   /* Make sure we have the only reference */
104   if (g_object_is_floating (object))
105     g_object_ref_sink (object);
106 
107   /* Assert that the object finalizes properly */
108   g_object_weak_ref (object, check_finalized, &finalized);
109 
110   /* Toplevels are owned by GTK, just tell GTK to destroy it */
111   if (GTK_IS_WINDOW (object))
112     gtk_window_destroy (GTK_WINDOW (object));
113   else
114     g_object_unref (object);
115 
116   /* Even if the object did finalize, it may have left some dangerous stuff in the GMainContext */
117   done = FALSE;
118   g_timeout_add (50, main_loop_quit_cb, &done);
119   while (!done)
120     g_main_context_iteration (NULL, TRUE);
121 }
122 
123 static gboolean
dbind_warning_handler(const char * log_domain,GLogLevelFlags log_level,const char * message,gpointer user_data)124 dbind_warning_handler (const char     *log_domain,
125                        GLogLevelFlags  log_level,
126                        const char     *message,
127                        gpointer        user_data)
128 {
129   if (strcmp (log_domain, "dbind") == 0 &&
130       log_level == (G_LOG_LEVEL_WARNING|G_LOG_FLAG_FATAL))
131     return FALSE;
132 
133   return TRUE;
134 }
135 
136 int
main(int argc,char ** argv)137 main (int argc, char **argv)
138 {
139   const GType *all_types;
140   guint n_types = 0, i;
141   int result;
142   const char *display, *x_r_d;
143 
144   /* These must be set before gtk_test_init */
145   g_setenv ("GIO_USE_VFS", "local", TRUE);
146   g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
147 
148   /* g_test_dbus_up() helpfully clears these, so we have to re-set it */
149   display = g_getenv ("DISPLAY");
150   x_r_d = g_getenv ("XDG_RUNTIME_DIR");
151 
152   if (display)
153     g_setenv ("DISPLAY", display, TRUE);
154   if (x_r_d)
155     g_setenv ("XDG_RUNTIME_DIR", x_r_d, TRUE);
156 
157   g_test_log_set_fatal_handler (dbind_warning_handler, NULL);
158 
159   /* initialize test program */
160   gtk_test_init (&argc, &argv);
161   gtk_test_register_all_types ();
162 
163   all_types = gtk_test_list_all_types (&n_types);
164 
165   for (i = 0; i < n_types; i++)
166     {
167       if (g_type_is_a (all_types[i], G_TYPE_OBJECT) &&
168 	  G_TYPE_IS_INSTANTIATABLE (all_types[i]) &&
169 	  !G_TYPE_IS_ABSTRACT (all_types[i]) &&
170 #ifdef GDK_WINDOWING_X11
171 	  all_types[i] != GDK_TYPE_X11_SURFACE &&
172 	  all_types[i] != GDK_TYPE_X11_SCREEN &&
173 	  all_types[i] != GDK_TYPE_X11_DISPLAY &&
174 	  all_types[i] != GDK_TYPE_X11_DEVICE_MANAGER_XI2 &&
175 	  all_types[i] != GDK_TYPE_X11_GL_CONTEXT &&
176 #endif
177 	  /* Not allowed to finalize a GdkPixbufLoader without calling gdk_pixbuf_loader_close() */
178 	  all_types[i] != GDK_TYPE_PIXBUF_LOADER &&
179 	  all_types[i] != gdk_pixbuf_simple_anim_iter_get_type() &&
180           !g_type_is_a (all_types[i], GTK_TYPE_SHORTCUT_TRIGGER) &&
181           !g_type_is_a (all_types[i], GTK_TYPE_SHORTCUT_ACTION))
182 	{
183 	  char *test_path = g_strdup_printf ("/FinalizeObject/%s", g_type_name (all_types[i]));
184 
185 	  g_test_add_data_func (test_path, GSIZE_TO_POINTER (all_types[i]), test_finalize_object);
186 
187 	  g_free (test_path);
188 	}
189     }
190 
191   result = g_test_run();
192 
193   return result;
194 }
195