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 |
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 |
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 |
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 (v1: g_type_name (type: test_type), v2: "GdkClipboard" )) |
61 | object = g_object_new (object_type: test_type, first_property_name: "display" , gdk_display_get_default (), NULL); |
62 | else if (g_str_equal (v1: g_type_name (type: test_type), v2: "GdkDrag" ) || |
63 | g_str_equal (v1: g_type_name (type: test_type), v2: "GdkDrop" )) |
64 | { |
65 | GdkContentFormats *formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING); |
66 | object = g_object_new (object_type: test_type, |
67 | first_property_name: "device" , gdk_seat_get_pointer (seat: gdk_display_get_default_seat (display: gdk_display_get_default ())), |
68 | "formats" , formats, |
69 | NULL); |
70 | gdk_content_formats_unref (formats); |
71 | } |
72 | else if (g_type_is_a (type: test_type, GSK_TYPE_GL_SHADER)) |
73 | { |
74 | GBytes *bytes = g_bytes_new_static (data: "" , size: 0); |
75 | object = g_object_new (object_type: test_type, first_property_name: "source" , bytes, NULL); |
76 | g_bytes_unref (bytes); |
77 | } |
78 | else if (g_type_is_a (type: test_type, GTK_TYPE_FILTER_LIST_MODEL) || |
79 | g_type_is_a (type: test_type, GTK_TYPE_NO_SELECTION) || |
80 | g_type_is_a (type: test_type, GTK_TYPE_SINGLE_SELECTION) || |
81 | g_type_is_a (type: test_type, GTK_TYPE_MULTI_SELECTION)) |
82 | { |
83 | GListStore *list_store = g_list_store_new (G_TYPE_OBJECT); |
84 | object = g_object_new (object_type: test_type, |
85 | first_property_name: "model" , list_store, |
86 | NULL); |
87 | g_object_unref (object: list_store); |
88 | } |
89 | else if (g_type_is_a (type: 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 (object_type: 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, notify: check_finalized, data: &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 (interval: 50, function: main_loop_quit_cb, data: &done); |
119 | while (!done) |
120 | g_main_context_iteration (NULL, TRUE); |
121 | } |
122 | |
123 | static gboolean |
124 | dbind_warning_handler (const char *log_domain, |
125 | GLogLevelFlags log_level, |
126 | const char *message, |
127 | gpointer user_data) |
128 | { |
129 | if (strcmp (s1: log_domain, s2: "dbind" ) == 0 && |
130 | log_level == (G_LOG_LEVEL_WARNING|G_LOG_FLAG_FATAL)) |
131 | return FALSE; |
132 | |
133 | return TRUE; |
134 | } |
135 | |
136 | int |
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 (variable: "GIO_USE_VFS" , value: "local" , TRUE); |
146 | g_setenv (variable: "GSETTINGS_BACKEND" , value: "memory" , TRUE); |
147 | |
148 | /* g_test_dbus_up() helpfully clears these, so we have to re-set it */ |
149 | display = g_getenv (variable: "DISPLAY" ); |
150 | x_r_d = g_getenv (variable: "XDG_RUNTIME_DIR" ); |
151 | |
152 | if (display) |
153 | g_setenv (variable: "DISPLAY" , value: display, TRUE); |
154 | if (x_r_d) |
155 | g_setenv (variable: "XDG_RUNTIME_DIR" , value: x_r_d, TRUE); |
156 | |
157 | g_test_log_set_fatal_handler (log_func: dbind_warning_handler, NULL); |
158 | |
159 | /* initialize test program */ |
160 | gtk_test_init (argcp: &argc, argvp: &argv); |
161 | gtk_test_register_all_types (); |
162 | |
163 | all_types = gtk_test_list_all_types (n_types: &n_types); |
164 | |
165 | for (i = 0; i < n_types; i++) |
166 | { |
167 | if (g_type_is_a (type: 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 (type: all_types[i], GTK_TYPE_SHORTCUT_TRIGGER) && |
181 | !g_type_is_a (type: all_types[i], GTK_TYPE_SHORTCUT_ACTION)) |
182 | { |
183 | char *test_path = g_strdup_printf (format: "/FinalizeObject/%s" , g_type_name (type: all_types[i])); |
184 | |
185 | g_test_add_data_func (testpath: test_path, GSIZE_TO_POINTER (all_types[i]), test_func: test_finalize_object); |
186 | |
187 | g_free (mem: test_path); |
188 | } |
189 | } |
190 | |
191 | result = g_test_run(); |
192 | |
193 | return result; |
194 | } |
195 | |