1 | /* Gtk+ property notify tests |
2 | * Copyright (C) 2014 Matthias Clasen |
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 | |
18 | #include <math.h> |
19 | #include <string.h> |
20 | #include <gtk/gtk.h> |
21 | #include <gtk/gtkunixprint.h> |
22 | #ifdef GDK_WINDOWING_WAYLAND |
23 | #include "gdk/wayland/gdkwayland.h" |
24 | #endif |
25 | |
26 | static void |
27 | assert_notifies (GObject *object, |
28 | const char *property, |
29 | guint counted, |
30 | guint expected) |
31 | { |
32 | if (expected == counted) |
33 | return; |
34 | |
35 | g_test_message (format: "ERROR: While testing %s::%s: %u notify emissions expected, but got %u" , |
36 | G_OBJECT_TYPE_NAME (object), property, |
37 | expected, counted); |
38 | g_test_fail (); |
39 | } |
40 | |
41 | typedef struct |
42 | { |
43 | const char *name; |
44 | int count; |
45 | } NotifyData; |
46 | |
47 | static void |
48 | count_notify (GObject *obj, GParamSpec *pspec, NotifyData *data) |
49 | { |
50 | if (g_strcmp0 (str1: data->name, str2: pspec->name) == 0) |
51 | data->count++; |
52 | } |
53 | |
54 | /* Check that we get notifications when properties change. |
55 | * Also check that we don't emit redundant notifications for |
56 | * enum, flags, booleans, ints. We allow redundant notifications |
57 | * for strings, and floats |
58 | */ |
59 | static void |
60 | check_property (GObject *instance, GParamSpec *pspec) |
61 | { |
62 | g_test_message (format: "Checking %s:%s" , G_OBJECT_TYPE_NAME (instance), pspec->name); |
63 | |
64 | if (G_TYPE_IS_ENUM (pspec->value_type)) |
65 | { |
66 | GEnumClass *class; |
67 | int i; |
68 | NotifyData data; |
69 | gulong id; |
70 | int first; |
71 | int value; |
72 | int current_count; |
73 | |
74 | class = g_type_class_ref (type: pspec->value_type); |
75 | |
76 | data.name = pspec->name; |
77 | data.count = 0; |
78 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
79 | |
80 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
81 | g_object_set (object: instance, first_property_name: pspec->name, value, NULL); |
82 | |
83 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 0); |
84 | |
85 | if (class->values[0].value == value) |
86 | first = 1; |
87 | else |
88 | first = 0; |
89 | |
90 | for (i = first; i < class->n_values; i++) |
91 | { |
92 | current_count = data.count + 1; |
93 | g_object_set (object: instance, first_property_name: pspec->name, class->values[i].value, NULL); |
94 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: current_count); |
95 | |
96 | if (current_count == 10) /* just test a few */ |
97 | break; |
98 | } |
99 | |
100 | g_signal_handler_disconnect (instance, handler_id: id); |
101 | g_type_class_unref (g_class: class); |
102 | } |
103 | else if (G_TYPE_IS_FLAGS (pspec->value_type)) |
104 | { |
105 | GFlagsClass *class; |
106 | int i; |
107 | NotifyData data; |
108 | gulong id; |
109 | guint value; |
110 | int current_count; |
111 | |
112 | class = g_type_class_ref (type: pspec->value_type); |
113 | |
114 | data.name = pspec->name; |
115 | data.count = 0; |
116 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
117 | |
118 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
119 | g_object_set (object: instance, first_property_name: pspec->name, value, NULL); |
120 | |
121 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 0); |
122 | |
123 | for (i = 0; i < class->n_values; i++) |
124 | { |
125 | /* some flags have a 'none' member, skip it */ |
126 | if (class->values[i].value == 0) |
127 | continue; |
128 | |
129 | if ((value & class->values[i].value) != 0) |
130 | continue; |
131 | |
132 | value |= class->values[i].value; |
133 | current_count = data.count + 1; |
134 | g_object_set (object: instance, first_property_name: pspec->name, value, NULL); |
135 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: current_count); |
136 | |
137 | if (current_count == 10) /* just test a few */ |
138 | break; |
139 | } |
140 | |
141 | g_signal_handler_disconnect (instance, handler_id: id); |
142 | g_type_class_unref (g_class: class); |
143 | } |
144 | else if (pspec->value_type == G_TYPE_BOOLEAN) |
145 | { |
146 | NotifyData data; |
147 | gboolean value; |
148 | gulong id; |
149 | |
150 | data.name = pspec->name; |
151 | data.count = 0; |
152 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
153 | |
154 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
155 | g_object_set (object: instance, first_property_name: pspec->name, value, NULL); |
156 | |
157 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 0); |
158 | |
159 | g_object_set (object: instance, first_property_name: pspec->name, 1 - value, NULL); |
160 | |
161 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 1); |
162 | |
163 | g_signal_handler_disconnect (instance, handler_id: id); |
164 | } |
165 | else if (pspec->value_type == G_TYPE_INT) |
166 | { |
167 | GParamSpecInt *p = G_PARAM_SPEC_INT (pspec); |
168 | int i; |
169 | NotifyData data; |
170 | gulong id; |
171 | int value; |
172 | int current_count; |
173 | |
174 | data.name = pspec->name; |
175 | data.count = 0; |
176 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
177 | |
178 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
179 | g_object_set (object: instance, first_property_name: pspec->name, value, NULL); |
180 | |
181 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 0); |
182 | |
183 | for (i = p->minimum; i <= p->maximum; i++) |
184 | { |
185 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
186 | if (value == i) |
187 | continue; |
188 | |
189 | current_count = data.count + 1; |
190 | g_object_set (object: instance, first_property_name: pspec->name, i, NULL); |
191 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: current_count); |
192 | |
193 | if (current_count == 10) /* just test a few */ |
194 | break; |
195 | } |
196 | |
197 | g_signal_handler_disconnect (instance, handler_id: id); |
198 | } |
199 | else if (pspec->value_type == G_TYPE_UINT) |
200 | { |
201 | guint i; |
202 | NotifyData data; |
203 | gulong id; |
204 | guint value; |
205 | int current_count; |
206 | guint minimum, maximum; |
207 | |
208 | if (G_IS_PARAM_SPEC_UINT (pspec)) |
209 | { |
210 | minimum = G_PARAM_SPEC_UINT (pspec)->minimum; |
211 | maximum = G_PARAM_SPEC_UINT (pspec)->maximum; |
212 | } |
213 | else /* random */ |
214 | { |
215 | minimum = 0; |
216 | maximum = 1000; |
217 | } |
218 | |
219 | data.name = pspec->name; |
220 | data.count = 0; |
221 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
222 | |
223 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
224 | g_object_set (object: instance, first_property_name: pspec->name, value, NULL); |
225 | |
226 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 0); |
227 | |
228 | for (i = minimum; i <= maximum; i++) |
229 | { |
230 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
231 | if (value == i) |
232 | continue; |
233 | |
234 | current_count = data.count + 1; |
235 | g_object_set (object: instance, first_property_name: pspec->name, i, NULL); |
236 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: current_count); |
237 | |
238 | if (current_count == 10) /* just test a few */ |
239 | break; |
240 | } |
241 | |
242 | g_signal_handler_disconnect (instance, handler_id: id); |
243 | } |
244 | else if (pspec->value_type == G_TYPE_STRING) |
245 | { |
246 | NotifyData data; |
247 | gulong id; |
248 | char *value; |
249 | char *new_value; |
250 | |
251 | data.name = pspec->name; |
252 | data.count = 0; |
253 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
254 | |
255 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
256 | /* don't check redundant notifications */ |
257 | |
258 | new_value = g_strconcat (string1: "(" , value, "." , value, ")" , NULL); |
259 | |
260 | g_object_set (object: instance, first_property_name: pspec->name, new_value, NULL); |
261 | |
262 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: 1); |
263 | |
264 | g_free (mem: value); |
265 | g_free (mem: new_value); |
266 | |
267 | g_signal_handler_disconnect (instance, handler_id: id); |
268 | } |
269 | else if (pspec->value_type == G_TYPE_DOUBLE) |
270 | { |
271 | GParamSpecDouble *p = G_PARAM_SPEC_DOUBLE (pspec); |
272 | guint i; |
273 | NotifyData data; |
274 | gulong id; |
275 | double value; |
276 | double new_value; |
277 | int current_count; |
278 | double delta; |
279 | |
280 | data.name = pspec->name; |
281 | data.count = 0; |
282 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
283 | |
284 | /* don't check redundant notifications */ |
285 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
286 | |
287 | if (p->maximum > 100 || p->minimum < -100) |
288 | delta = M_PI; |
289 | else |
290 | delta = (p->maximum - p->minimum) / 10.0; |
291 | |
292 | new_value = p->minimum; |
293 | for (i = 0; i < 10; i++) |
294 | { |
295 | new_value += delta; |
296 | |
297 | if (fabs (x: value - new_value) < p->epsilon) |
298 | continue; |
299 | |
300 | if (new_value > p->maximum) |
301 | break; |
302 | |
303 | current_count = data.count + 1; |
304 | g_object_set (object: instance, first_property_name: pspec->name, new_value, NULL); |
305 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: current_count); |
306 | } |
307 | |
308 | g_signal_handler_disconnect (instance, handler_id: id); |
309 | } |
310 | else if (pspec->value_type == G_TYPE_FLOAT) |
311 | { |
312 | GParamSpecFloat *p = G_PARAM_SPEC_FLOAT (pspec); |
313 | guint i; |
314 | NotifyData data; |
315 | gulong id; |
316 | float value; |
317 | float new_value; |
318 | int current_count; |
319 | |
320 | data.name = pspec->name; |
321 | data.count = 0; |
322 | id = g_signal_connect (instance, "notify" , G_CALLBACK (count_notify), &data); |
323 | |
324 | /* don't check redundant notifications */ |
325 | g_object_get (object: instance, first_property_name: pspec->name, &value, NULL); |
326 | |
327 | new_value = p->minimum; |
328 | for (i = 0; i < 10; i++) |
329 | { |
330 | if (fabs (x: value - new_value) < p->epsilon) |
331 | continue; |
332 | |
333 | current_count = data.count + 1; |
334 | new_value += (p->maximum - p->minimum) / 10.0; |
335 | |
336 | if (new_value > p->maximum) |
337 | break; |
338 | |
339 | g_object_set (object: instance, first_property_name: pspec->name, new_value, NULL); |
340 | assert_notifies (object: instance, property: pspec->name, counted: data.count, expected: current_count); |
341 | } |
342 | |
343 | g_signal_handler_disconnect (instance, handler_id: id); |
344 | } |
345 | else |
346 | { |
347 | if (g_test_verbose ()) |
348 | g_print (format: "Skipping property %s.%s of type %s\n" , g_type_name (type: pspec->owner_type), pspec->name, g_type_name (type: pspec->value_type)); |
349 | } |
350 | } |
351 | |
352 | static void |
353 | test_type (gconstpointer data) |
354 | { |
355 | GObjectClass *klass; |
356 | GObject *instance; |
357 | GParamSpec **pspecs; |
358 | guint n_pspecs, i; |
359 | GType type; |
360 | GdkDisplay *display; |
361 | |
362 | type = * (GType *) data; |
363 | |
364 | display = gdk_display_get_default (); |
365 | |
366 | if (!G_TYPE_IS_CLASSED (type)) |
367 | return; |
368 | |
369 | if (G_TYPE_IS_ABSTRACT (type)) |
370 | return; |
371 | |
372 | if (!g_type_is_a (type, G_TYPE_OBJECT)) |
373 | return; |
374 | |
375 | /* non-GTK */ |
376 | if (g_str_equal (v1: g_type_name (type), v2: "GdkPixbufSimpleAnim" )) |
377 | return; |
378 | |
379 | /* Deprecated, not getting fixed */ |
380 | if (g_str_equal (v1: g_type_name (type), v2: "GtkColorSelection" ) || |
381 | g_str_equal (v1: g_type_name (type), v2: "GtkNumerableIcon" )) |
382 | return; |
383 | |
384 | /* These can't be freely constructed/destroyed */ |
385 | if (g_type_is_a (type, GTK_TYPE_APPLICATION) || |
386 | g_type_is_a (type, GDK_TYPE_PIXBUF_LOADER) || |
387 | g_type_is_a (type, GTK_TYPE_LAYOUT_CHILD) || |
388 | #ifdef G_OS_UNIX |
389 | g_type_is_a (type, GTK_TYPE_PRINT_JOB) || |
390 | #endif |
391 | g_type_is_a (type, is_a_type: gdk_pixbuf_simple_anim_iter_get_type ()) || |
392 | g_str_equal (v1: g_type_name (type), v2: "GdkX11DeviceManagerXI2" ) || |
393 | g_str_equal (v1: g_type_name (type), v2: "GdkX11DeviceManagerCore" ) || |
394 | g_str_equal (v1: g_type_name (type), v2: "GdkX11Display" ) || |
395 | g_str_equal (v1: g_type_name (type), v2: "GdkX11Screen" ) || |
396 | g_str_equal (v1: g_type_name (type), v2: "GdkX11GLContext" )) |
397 | return; |
398 | |
399 | /* This throws a critical when the connection is dropped */ |
400 | if (g_type_is_a (type, GTK_TYPE_APP_CHOOSER_DIALOG)) |
401 | return; |
402 | |
403 | /* These leak their GDBusConnections */ |
404 | if (g_type_is_a (type, GTK_TYPE_FILE_CHOOSER_DIALOG) || |
405 | g_type_is_a (type, GTK_TYPE_FILE_CHOOSER_WIDGET) || |
406 | g_type_is_a (type, GTK_TYPE_FILE_CHOOSER_NATIVE)) |
407 | return; |
408 | |
409 | if (g_str_equal (v1: g_type_name (type), v2: "GtkPlacesSidebar" )) |
410 | return; |
411 | |
412 | /* These rely on a d-bus session bus */ |
413 | if (g_type_is_a (type, GTK_TYPE_MOUNT_OPERATION)) |
414 | return; |
415 | |
416 | /* Needs a special surface */ |
417 | if (g_type_is_a (type, GTK_TYPE_DRAG_ICON)) |
418 | return; |
419 | |
420 | /* these assert in constructed */ |
421 | if (g_type_is_a (type, GTK_TYPE_ALTERNATIVE_TRIGGER) || |
422 | g_type_is_a (type, GTK_TYPE_SIGNAL_ACTION) || |
423 | g_type_is_a (type, GTK_TYPE_NAMED_ACTION)) |
424 | return; |
425 | |
426 | klass = g_type_class_ref (type); |
427 | |
428 | if (g_type_is_a (type, GTK_TYPE_SETTINGS)) |
429 | instance = G_OBJECT (g_object_ref (gtk_settings_get_default ())); |
430 | else if (g_type_is_a (type, GDK_TYPE_SURFACE)) |
431 | { |
432 | instance = G_OBJECT (g_object_ref (gdk_surface_new_toplevel (display))); |
433 | } |
434 | else if (g_str_equal (v1: g_type_name (type), v2: "GdkX11Cursor" )) |
435 | instance = g_object_new (object_type: type, first_property_name: "display" , display, NULL); |
436 | else if (g_str_equal (v1: g_type_name (type), v2: "GdkClipboard" )) |
437 | instance = g_object_new (object_type: type, first_property_name: "display" , display, NULL); |
438 | else if (g_str_equal (v1: g_type_name (type), v2: "GdkDrag" )) |
439 | { |
440 | GdkContentFormats *formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING); |
441 | instance = g_object_new (object_type: type, |
442 | first_property_name: "device" , gdk_seat_get_pointer (seat: gdk_display_get_default_seat (display: gdk_display_get_default ())), |
443 | "formats" , formats, |
444 | NULL); |
445 | gdk_content_formats_unref (formats); |
446 | } |
447 | else if (g_str_equal (v1: g_type_name (type), v2: "GdkDrop" )) |
448 | { |
449 | GdkContentFormats *formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING); |
450 | instance = g_object_new (object_type: type, |
451 | first_property_name: "device" , gdk_seat_get_pointer (seat: gdk_display_get_default_seat (display: gdk_display_get_default ())), |
452 | "formats" , formats, |
453 | NULL); |
454 | gdk_content_formats_unref (formats); |
455 | } |
456 | else if (g_type_is_a (type, GSK_TYPE_GL_SHADER)) |
457 | { |
458 | GBytes *bytes = g_bytes_new_static (data: "" , size: 0); |
459 | instance = g_object_new (object_type: type, first_property_name: "source" , bytes, NULL); |
460 | g_bytes_unref (bytes); |
461 | } |
462 | else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) || |
463 | g_type_is_a (type, GTK_TYPE_NO_SELECTION) || |
464 | g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) || |
465 | g_type_is_a (type, GTK_TYPE_MULTI_SELECTION)) |
466 | { |
467 | GListStore *list_store = g_list_store_new (G_TYPE_OBJECT); |
468 | instance = g_object_new (object_type: type, |
469 | first_property_name: "model" , list_store, |
470 | NULL); |
471 | g_object_unref (object: list_store); |
472 | } |
473 | else if (g_type_is_a (type, GTK_TYPE_CALENDAR)) |
474 | { |
475 | /* avoid day 30 and 31, since they don't exist in February */ |
476 | instance = g_object_new (object_type: type, |
477 | first_property_name: "year" , 1984, |
478 | "month" , 10, |
479 | "day" , 05, |
480 | NULL); |
481 | } |
482 | /* special casing for singletons */ |
483 | else if (g_type_is_a (type, GTK_TYPE_NEVER_TRIGGER)) |
484 | instance = (GObject *) g_object_ref (gtk_never_trigger_get ()); |
485 | else if (g_type_is_a (type, GTK_TYPE_NOTHING_ACTION)) |
486 | instance = (GObject *) g_object_ref (gtk_nothing_action_get ()); |
487 | else if (g_type_is_a (type, GTK_TYPE_ACTIVATE_ACTION)) |
488 | instance = (GObject *) g_object_ref (gtk_activate_action_get ()); |
489 | else if (g_type_is_a (type, GTK_TYPE_MNEMONIC_ACTION)) |
490 | instance = (GObject *) g_object_ref (gtk_mnemonic_action_get ()); |
491 | else |
492 | instance = g_object_new (object_type: type, NULL); |
493 | |
494 | if (g_type_is_a (type, G_TYPE_INITIALLY_UNOWNED)) |
495 | g_object_ref_sink (instance); |
496 | |
497 | pspecs = g_object_class_list_properties (oclass: klass, n_properties: &n_pspecs); |
498 | for (i = 0; i < n_pspecs; ++i) |
499 | { |
500 | GParamSpec *pspec = pspecs[i]; |
501 | |
502 | if ((pspec->flags & G_PARAM_READABLE) == 0) |
503 | continue; |
504 | |
505 | if ((pspec->flags & G_PARAM_WRITABLE) == 0) |
506 | continue; |
507 | |
508 | if ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) != 0) |
509 | continue; |
510 | |
511 | /* non-GTK */ |
512 | if (g_str_equal (v1: g_type_name (type: pspec->owner_type), v2: "GdkPixbufSimpleAnim" ) || |
513 | g_str_equal (v1: g_type_name (type: pspec->owner_type), v2: "GMountOperation" )) |
514 | continue; |
515 | |
516 | /* set properties are best skipped */ |
517 | if (pspec->value_type == G_TYPE_BOOLEAN && |
518 | g_str_has_suffix (str: pspec->name, suffix: "-set" )) |
519 | continue; |
520 | |
521 | /* These are special */ |
522 | if (g_type_is_a (type: pspec->owner_type, GTK_TYPE_WIDGET) && |
523 | (g_str_equal (v1: pspec->name, v2: "has-focus" ) || |
524 | g_str_equal (v1: pspec->name, v2: "has-default" ) || |
525 | g_str_equal (v1: pspec->name, v2: "is-focus" ) || |
526 | g_str_equal (v1: pspec->name, v2: "hexpand" ) || |
527 | g_str_equal (v1: pspec->name, v2: "vexpand" ) || |
528 | g_str_equal (v1: pspec->name, v2: "visible" ))) |
529 | continue; |
530 | |
531 | if (g_type_is_a (type, GTK_TYPE_ACCESSIBLE) && |
532 | g_str_equal (v1: pspec->name, v2: "accessible-role" )) |
533 | continue; |
534 | |
535 | if (pspec->owner_type == GTK_TYPE_ENTRY && |
536 | g_str_equal (v1: pspec->name, v2: "im-module" )) |
537 | continue; |
538 | |
539 | if (type == GTK_TYPE_SETTINGS) |
540 | continue; |
541 | |
542 | if (g_type_is_a (type: pspec->owner_type, GTK_TYPE_ENTRY_COMPLETION) && |
543 | g_str_equal (v1: pspec->name, v2: "text-column" )) |
544 | continue; |
545 | |
546 | if (g_type_is_a (type: pspec->owner_type, GTK_TYPE_COLOR_CHOOSER) && |
547 | g_str_equal (v1: pspec->name, v2: "show-editor" )) |
548 | continue; |
549 | |
550 | if (g_type_is_a (type: pspec->owner_type, GTK_TYPE_NOTEBOOK) && |
551 | g_str_equal (v1: pspec->name, v2: "page" )) |
552 | continue; |
553 | |
554 | /* Too many special cases involving -set properties */ |
555 | if (g_str_equal (v1: g_type_name (type: pspec->owner_type), v2: "GtkCellRendererText" ) || |
556 | g_str_equal (v1: g_type_name (type: pspec->owner_type), v2: "GtkTextTag" )) |
557 | continue; |
558 | |
559 | /* Most things assume a model is set */ |
560 | if (g_str_equal (v1: g_type_name (type: pspec->owner_type), v2: "GtkComboBox" )) |
561 | continue; |
562 | |
563 | /* Can only be set on unmapped windows */ |
564 | if (pspec->owner_type == GTK_TYPE_WINDOW && |
565 | g_str_equal (v1: pspec->name, v2: "type-hint" )) |
566 | continue; |
567 | |
568 | /* Special restrictions on allowed values */ |
569 | if (pspec->owner_type == GTK_TYPE_COMBO_BOX && |
570 | (g_str_equal (v1: pspec->name, v2: "id-column" ) || |
571 | g_str_equal (v1: pspec->name, v2: "active-id" ) || |
572 | g_str_equal (v1: pspec->name, v2: "entry-text-column" ))) |
573 | continue; |
574 | |
575 | if (pspec->owner_type == GTK_TYPE_ENTRY_COMPLETION && |
576 | g_str_equal (v1: pspec->name, v2: "text-column" )) |
577 | continue; |
578 | |
579 | if (pspec->owner_type == GTK_TYPE_PRINT_OPERATION && |
580 | (g_str_equal (v1: pspec->name, v2: "current-page" ) || |
581 | g_str_equal (v1: pspec->name, v2: "n-pages" ))) |
582 | continue; |
583 | |
584 | if (pspec->owner_type == GTK_TYPE_RANGE && |
585 | g_str_equal (v1: pspec->name, v2: "fill-level" )) |
586 | continue; |
587 | |
588 | if (pspec->owner_type == GTK_TYPE_SPIN_BUTTON && |
589 | g_str_equal (v1: pspec->name, v2: "value" )) |
590 | continue; |
591 | |
592 | if (pspec->owner_type == GTK_TYPE_STACK && |
593 | g_str_equal (v1: pspec->name, v2: "visible-child-name" )) |
594 | continue; |
595 | |
596 | if (pspec->owner_type == GTK_TYPE_STACK_PAGE && /* Can't change position without a stack */ |
597 | g_str_equal (v1: pspec->name, v2: "position" )) |
598 | continue; |
599 | |
600 | /* Can't realize a popover without a parent */ |
601 | if (g_type_is_a (type, GTK_TYPE_POPOVER) && |
602 | g_str_equal (v1: pspec->name, v2: "visible" )) |
603 | continue; |
604 | |
605 | if (pspec->owner_type == GTK_TYPE_POPOVER_MENU && |
606 | g_str_equal (v1: pspec->name, v2: "visible-submenu" )) |
607 | continue; |
608 | |
609 | if (pspec->owner_type == GTK_TYPE_TEXT_VIEW && |
610 | g_str_equal (v1: pspec->name, v2: "im-module" )) |
611 | continue; |
612 | |
613 | if (pspec->owner_type == GTK_TYPE_TREE_SELECTION && |
614 | g_str_equal (v1: pspec->name, v2: "mode" )) /* requires a treeview */ |
615 | continue; |
616 | |
617 | if (pspec->owner_type == GTK_TYPE_TREE_VIEW && |
618 | g_str_equal (v1: pspec->name, v2: "headers-clickable" )) /* requires columns */ |
619 | continue; |
620 | |
621 | /* This one has a special-purpose default value */ |
622 | if (g_type_is_a (type, GTK_TYPE_DIALOG) && |
623 | g_str_equal (v1: pspec->name, v2: "use-header-bar" )) |
624 | continue; |
625 | |
626 | if (g_type_is_a (type, GTK_TYPE_ASSISTANT) && |
627 | g_str_equal (v1: pspec->name, v2: "use-header-bar" )) |
628 | continue; |
629 | |
630 | if (g_type_is_a (type, GTK_TYPE_SHORTCUTS_SHORTCUT) && |
631 | g_str_equal (v1: pspec->name, v2: "accelerator" )) |
632 | continue; |
633 | |
634 | if (g_type_is_a (type, GTK_TYPE_SHORTCUT_LABEL) && |
635 | g_str_equal (v1: pspec->name, v2: "accelerator" )) |
636 | continue; |
637 | |
638 | if (g_type_is_a (type, GTK_TYPE_FONT_CHOOSER) && |
639 | g_str_equal (v1: pspec->name, v2: "font" )) |
640 | continue; |
641 | |
642 | /* these depend on the min-content- properties in a way that breaks our test */ |
643 | if (g_type_is_a (type, GTK_TYPE_SCROLLED_WINDOW) && |
644 | (g_str_equal (v1: pspec->name, v2: "max-content-width" ) || |
645 | g_str_equal (v1: pspec->name, v2: "max-content-height" ))) |
646 | continue; |
647 | |
648 | /* expanding only works if rows are expandable */ |
649 | if (g_type_is_a (type, GTK_TYPE_TREE_LIST_ROW) && |
650 | g_str_equal (v1: pspec->name, v2: "expanded" )) |
651 | continue; |
652 | |
653 | /* can't select items without an underlying, populated model */ |
654 | if (g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) && |
655 | (g_str_equal (v1: pspec->name, v2: "selected" ) || |
656 | g_str_equal (v1: pspec->name, v2: "selected-item" ))) |
657 | continue; |
658 | |
659 | /* can't select items without an underlying, populated model */ |
660 | if (g_type_is_a (type, GTK_TYPE_DROP_DOWN) && |
661 | g_str_equal (v1: pspec->name, v2: "selected" )) |
662 | continue; |
663 | |
664 | /* can't set position without a notebook */ |
665 | if (g_type_is_a (type, GTK_TYPE_NOTEBOOK_PAGE) && |
666 | g_str_equal (v1: pspec->name, v2: "position" )) |
667 | continue; |
668 | |
669 | if (g_test_verbose ()) |
670 | g_print (format: "Property %s.%s\n" , g_type_name (type: pspec->owner_type), pspec->name); |
671 | |
672 | check_property (instance, pspec); |
673 | } |
674 | g_free (mem: pspecs); |
675 | |
676 | if (g_type_is_a (type, GDK_TYPE_SURFACE)) |
677 | gdk_surface_destroy (GDK_SURFACE (instance)); |
678 | else |
679 | g_object_unref (object: instance); |
680 | |
681 | g_type_class_unref (g_class: klass); |
682 | } |
683 | |
684 | int |
685 | main (int argc, char **argv) |
686 | { |
687 | const GType *otypes; |
688 | guint i; |
689 | int result; |
690 | |
691 | gtk_test_init (argcp: &argc, argvp: &argv); |
692 | gtk_test_register_all_types(); |
693 | |
694 | otypes = gtk_test_list_all_types (NULL); |
695 | for (i = 0; otypes[i]; i++) |
696 | { |
697 | char *testname; |
698 | |
699 | testname = g_strdup_printf (format: "/Notification/%s" , g_type_name (type: otypes[i])); |
700 | g_test_add_data_func (testpath: testname, test_data: &otypes[i], test_func: test_type); |
701 | g_free (mem: testname); |
702 | } |
703 | |
704 | result = g_test_run (); |
705 | |
706 | return result; |
707 | } |
708 | |