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
assert_notifies(GObject * object,const char * property,guint counted,guint expected)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 ("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
count_notify(GObject * obj,GParamSpec * pspec,NotifyData * data)48 count_notify (GObject *obj, GParamSpec *pspec, NotifyData *data)
49 {
50   if (g_strcmp0 (data->name, 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
check_property(GObject * instance,GParamSpec * pspec)60 check_property (GObject *instance, GParamSpec *pspec)
61 {
62   g_test_message ("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 (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 (instance, pspec->name, &value, NULL);
81       g_object_set (instance, pspec->name, value, NULL);
82 
83       assert_notifies (instance, pspec->name, data.count, 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 (instance, pspec->name, class->values[i].value, NULL);
94           assert_notifies (instance, pspec->name, data.count, current_count);
95 
96           if (current_count == 10) /* just test a few */
97             break;
98         }
99 
100       g_signal_handler_disconnect (instance, id);
101       g_type_class_unref (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 (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 (instance, pspec->name, &value, NULL);
119       g_object_set (instance, pspec->name, value, NULL);
120 
121       assert_notifies (instance, pspec->name, data.count, 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 (instance, pspec->name, value, NULL);
135           assert_notifies (instance, pspec->name, data.count, current_count);
136 
137           if (current_count == 10) /* just test a few */
138             break;
139         }
140 
141       g_signal_handler_disconnect (instance, id);
142       g_type_class_unref (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 (instance, pspec->name, &value, NULL);
155       g_object_set (instance, pspec->name, value, NULL);
156 
157       assert_notifies (instance, pspec->name, data.count, 0);
158 
159       g_object_set (instance, pspec->name, 1 - value, NULL);
160 
161       assert_notifies (instance, pspec->name, data.count, 1);
162 
163       g_signal_handler_disconnect (instance, 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 (instance, pspec->name, &value, NULL);
179       g_object_set (instance, pspec->name, value, NULL);
180 
181       assert_notifies (instance, pspec->name, data.count, 0);
182 
183       for (i = p->minimum; i <= p->maximum; i++)
184         {
185           g_object_get (instance, pspec->name, &value, NULL);
186           if (value == i)
187             continue;
188 
189           current_count = data.count + 1;
190           g_object_set (instance, pspec->name, i, NULL);
191           assert_notifies (instance, pspec->name, data.count, current_count);
192 
193           if (current_count == 10) /* just test a few */
194             break;
195         }
196 
197       g_signal_handler_disconnect (instance, 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 (instance, pspec->name, &value, NULL);
224       g_object_set (instance, pspec->name, value, NULL);
225 
226       assert_notifies (instance, pspec->name, data.count, 0);
227 
228       for (i = minimum; i <= maximum; i++)
229         {
230           g_object_get (instance, pspec->name, &value, NULL);
231           if (value == i)
232             continue;
233 
234           current_count = data.count + 1;
235           g_object_set (instance, pspec->name, i, NULL);
236           assert_notifies (instance, pspec->name, data.count, current_count);
237 
238           if (current_count == 10) /* just test a few */
239             break;
240         }
241 
242       g_signal_handler_disconnect (instance, 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 (instance, pspec->name, &value, NULL);
256       /* don't check redundant notifications */
257 
258       new_value = g_strconcat ("(", value, ".", value, ")", NULL);
259 
260       g_object_set (instance, pspec->name, new_value, NULL);
261 
262       assert_notifies (instance, pspec->name, data.count, 1);
263 
264       g_free (value);
265       g_free (new_value);
266 
267       g_signal_handler_disconnect (instance, 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 (instance, 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 (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 (instance, pspec->name, new_value, NULL);
305           assert_notifies (instance, pspec->name, data.count, current_count);
306         }
307 
308       g_signal_handler_disconnect (instance, 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 (instance, pspec->name, &value, NULL);
326 
327       new_value = p->minimum;
328       for (i = 0; i < 10; i++)
329         {
330           if (fabs (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 (instance, pspec->name, new_value, NULL);
340           assert_notifies (instance, pspec->name, data.count, current_count);
341         }
342 
343       g_signal_handler_disconnect (instance, id);
344     }
345   else
346     {
347       if (g_test_verbose ())
348         g_print ("Skipping property %s.%s of type %s\n", g_type_name (pspec->owner_type), pspec->name, g_type_name (pspec->value_type));
349     }
350 }
351 
352 static void
test_type(gconstpointer data)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 (g_type_name (type), "GdkPixbufSimpleAnim"))
377     return;
378 
379   /* Deprecated, not getting fixed */
380   if (g_str_equal (g_type_name (type), "GtkColorSelection") ||
381       g_str_equal (g_type_name (type), "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, gdk_pixbuf_simple_anim_iter_get_type ()) ||
392       g_str_equal (g_type_name (type), "GdkX11DeviceManagerXI2") ||
393       g_str_equal (g_type_name (type), "GdkX11DeviceManagerCore") ||
394       g_str_equal (g_type_name (type), "GdkX11Display") ||
395       g_str_equal (g_type_name (type), "GdkX11Screen") ||
396       g_str_equal (g_type_name (type), "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 (g_type_name (type), "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 (g_type_name (type), "GdkX11Cursor"))
435     instance = g_object_new (type, "display", display, NULL);
436   else if (g_str_equal (g_type_name (type), "GdkClipboard"))
437     instance = g_object_new (type, "display", display, NULL);
438   else if (g_str_equal (g_type_name (type), "GdkDrag"))
439     {
440       GdkContentFormats *formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
441       instance = g_object_new (type,
442                                "device", gdk_seat_get_pointer (gdk_display_get_default_seat (gdk_display_get_default ())),
443                                "formats", formats,
444                                NULL);
445       gdk_content_formats_unref (formats);
446     }
447   else if (g_str_equal (g_type_name (type), "GdkDrop"))
448     {
449       GdkContentFormats *formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
450       instance = g_object_new (type,
451                                "device", gdk_seat_get_pointer (gdk_display_get_default_seat (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 ("", 0);
459       instance = g_object_new (type, "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 (type,
469                                "model", list_store,
470                                NULL);
471       g_object_unref (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 (type,
477                                "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 (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 (klass, &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 (g_type_name (pspec->owner_type), "GdkPixbufSimpleAnim") ||
513           g_str_equal (g_type_name (pspec->owner_type), "GMountOperation"))
514         continue;
515 
516       /* set properties are best skipped */
517       if (pspec->value_type == G_TYPE_BOOLEAN &&
518           g_str_has_suffix (pspec->name, "-set"))
519         continue;
520 
521       /* These are special */
522       if (g_type_is_a (pspec->owner_type, GTK_TYPE_WIDGET) &&
523           (g_str_equal (pspec->name, "has-focus") ||
524            g_str_equal (pspec->name, "has-default") ||
525            g_str_equal (pspec->name, "is-focus") ||
526            g_str_equal (pspec->name, "hexpand") ||
527            g_str_equal (pspec->name, "vexpand") ||
528            g_str_equal (pspec->name, "visible")))
529         continue;
530 
531       if (g_type_is_a (type, GTK_TYPE_ACCESSIBLE) &&
532           g_str_equal (pspec->name, "accessible-role"))
533         continue;
534 
535       if (pspec->owner_type == GTK_TYPE_ENTRY &&
536           g_str_equal (pspec->name, "im-module"))
537         continue;
538 
539       if (type == GTK_TYPE_SETTINGS)
540         continue;
541 
542       if (g_type_is_a (pspec->owner_type, GTK_TYPE_ENTRY_COMPLETION) &&
543           g_str_equal (pspec->name, "text-column"))
544         continue;
545 
546       if (g_type_is_a (pspec->owner_type, GTK_TYPE_COLOR_CHOOSER) &&
547 	  g_str_equal (pspec->name, "show-editor"))
548         continue;
549 
550       if (g_type_is_a (pspec->owner_type, GTK_TYPE_NOTEBOOK) &&
551 	  g_str_equal (pspec->name, "page"))
552         continue;
553 
554       /* Too many special cases involving -set properties */
555       if (g_str_equal (g_type_name (pspec->owner_type), "GtkCellRendererText") ||
556           g_str_equal (g_type_name (pspec->owner_type), "GtkTextTag"))
557         continue;
558 
559       /* Most things assume a model is set */
560       if (g_str_equal (g_type_name (pspec->owner_type), "GtkComboBox"))
561         continue;
562 
563       /* Can only be set on unmapped windows */
564       if (pspec->owner_type == GTK_TYPE_WINDOW &&
565           g_str_equal (pspec->name, "type-hint"))
566         continue;
567 
568       /* Special restrictions on allowed values */
569       if (pspec->owner_type == GTK_TYPE_COMBO_BOX &&
570           (g_str_equal (pspec->name, "id-column") ||
571            g_str_equal (pspec->name, "active-id") ||
572            g_str_equal (pspec->name, "entry-text-column")))
573         continue;
574 
575       if (pspec->owner_type == GTK_TYPE_ENTRY_COMPLETION &&
576           g_str_equal (pspec->name, "text-column"))
577         continue;
578 
579       if (pspec->owner_type == GTK_TYPE_PRINT_OPERATION &&
580           (g_str_equal (pspec->name, "current-page") ||
581            g_str_equal (pspec->name, "n-pages")))
582         continue;
583 
584       if (pspec->owner_type == GTK_TYPE_RANGE &&
585           g_str_equal (pspec->name, "fill-level"))
586         continue;
587 
588       if (pspec->owner_type == GTK_TYPE_SPIN_BUTTON &&
589           g_str_equal (pspec->name, "value"))
590         continue;
591 
592       if (pspec->owner_type == GTK_TYPE_STACK &&
593           g_str_equal (pspec->name, "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 (pspec->name, "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 (pspec->name, "visible"))
603         continue;
604 
605       if (pspec->owner_type == GTK_TYPE_POPOVER_MENU &&
606           g_str_equal (pspec->name, "visible-submenu"))
607         continue;
608 
609       if (pspec->owner_type == GTK_TYPE_TEXT_VIEW &&
610           g_str_equal (pspec->name, "im-module"))
611         continue;
612 
613       if (pspec->owner_type == GTK_TYPE_TREE_SELECTION &&
614           g_str_equal (pspec->name, "mode")) /* requires a treeview */
615         continue;
616 
617       if (pspec->owner_type == GTK_TYPE_TREE_VIEW &&
618           g_str_equal (pspec->name, "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 (pspec->name, "use-header-bar"))
624 	continue;
625 
626       if (g_type_is_a (type, GTK_TYPE_ASSISTANT) &&
627 	  g_str_equal (pspec->name, "use-header-bar"))
628 	continue;
629 
630       if (g_type_is_a (type, GTK_TYPE_SHORTCUTS_SHORTCUT) &&
631 	  g_str_equal (pspec->name, "accelerator"))
632 	continue;
633 
634       if (g_type_is_a (type, GTK_TYPE_SHORTCUT_LABEL) &&
635 	  g_str_equal (pspec->name, "accelerator"))
636 	continue;
637 
638       if (g_type_is_a (type, GTK_TYPE_FONT_CHOOSER) &&
639 	  g_str_equal (pspec->name, "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 (pspec->name, "max-content-width") ||
645 	   g_str_equal (pspec->name, "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 (pspec->name, "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 (pspec->name, "selected") ||
656             g_str_equal (pspec->name, "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 (pspec->name, "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 (pspec->name, "position"))
667          continue;
668 
669       if (g_test_verbose ())
670         g_print ("Property %s.%s\n", g_type_name (pspec->owner_type), pspec->name);
671 
672       check_property (instance, pspec);
673     }
674   g_free (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 (instance);
680 
681   g_type_class_unref (klass);
682 }
683 
684 int
main(int argc,char ** argv)685 main (int argc, char **argv)
686 {
687   const GType *otypes;
688   guint i;
689   int result;
690 
691   gtk_test_init (&argc, &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 ("/Notification/%s", g_type_name (otypes[i]));
700       g_test_add_data_func (testname, &otypes[i], test_type);
701       g_free (testname);
702     }
703 
704   result = g_test_run ();
705 
706   return result;
707 }
708