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
26static void
27assert_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
41typedef struct
42{
43 const char *name;
44 int count;
45} NotifyData;
46
47static void
48count_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 */
59static void
60check_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
352static void
353test_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
684int
685main (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

source code of gtk/testsuite/gtk/notify.c