]> Pileus Git - ~andy/gtk/blob - gtk/tests/object.c
Forgotten file
[~andy/gtk] / gtk / tests / object.c
1 /* Gtk+ object tests
2  * Copyright (C) 2007 Imendio AB
3  * Authors: Tim Janik
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 #include <gtk/gtk.h>
21 #include <string.h>
22
23 /* --- helper macros for property value generation --- */
24 /* dvalue=+0: generate minimum value
25  * dvalue=.x: generate value within value range proportional to x.
26  * dvalue=+1: generate maximum value
27  * dvalue=-1: generate random value within value range
28  * dvalue=+2: initialize value from default_value
29  */
30 #define ASSIGN_VALUE(__g_value_set_func, __value, PSPECTYPE, __pspec, __default_value, __minimum, __maximum, __dvalue) do { \
31   PSPECTYPE __p = (PSPECTYPE) __pspec; \
32   __g_value_set_func (__value, SELECT_VALUE (__dvalue, __p->__default_value, __p->__minimum, __p->__maximum)); \
33 } while (0)
34 #define SELECT_VALUE(__dvalue, __default_value, __minimum, __maximum) ( \
35   __dvalue >= 0 && __dvalue <= 1 ? __minimum * (1 - __dvalue) + __dvalue * __maximum : \
36     __dvalue <= -1 ? g_test_rand_double_range (__minimum, __maximum) : \
37       __default_value)
38 #define SELECT_NAME(__dvalue) ( \
39   __dvalue == 0 ? "minimum" : \
40     __dvalue == 1 ? "maximum" : \
41       __dvalue >= +2 ? "default" : \
42         __dvalue == 0.5 ? "medium" : \
43           __dvalue > 0 && __dvalue < 1 ? "fractional" : \
44             "random")
45 #define MATCH_ANY_VALUE         ((void*) 0xf1874c23)
46
47 /* --- property blacklists --- */
48 typedef struct {
49   const char   *type_name;
50   const char   *name;
51   gconstpointer value;
52 } IgnoreProperty;
53 static const IgnoreProperty*
54 list_ignore_properties (gboolean buglist)
55 {
56   /* currently untestable properties */
57   static const IgnoreProperty ignore_properties[] = {
58     { "GtkContainer",           "child",                NULL, },                        /* needs working child widget */
59     { "GtkRadioMenuItem",       "group",                NULL, },                        /* needs working sibling */
60     { "GtkWidget",              "parent",               NULL, },                        /* needs working parent widget */
61     { "GtkCList",               "selection-mode",       (void*) GTK_SELECTION_NONE, },
62     { "GtkWidget",              "has-default",          (void*) TRUE, },                /* conflicts with toplevel-less widgets */
63     { "GtkWidget",              "screen",               NULL, },
64     { "GtkWindow",              "type-hint",            (void*) GDK_WINDOW_TYPE_HINT_DND, }, /* conflicts with ::visible=TRUE */
65     { "GtkCellView",            "background",           (void*) "", },                  /* "" is not a valid background color */
66     { "GtkColorButton",         "color",                (void*) NULL, },                /* not a valid boxed color */
67     { "GtkInputDialog",         "has-separator",        (void*) MATCH_ANY_VALUE, },     /* property disabled */
68     { "GtkMessageDialog",       "has-separator",        (void*) MATCH_ANY_VALUE, },     /* property disabled */
69     { "GtkFontSelectionDialog", "has-separator",        (void*) MATCH_ANY_VALUE, },     /* property disabled */
70     { "GtkColorSelectionDialog","has-separator",        (void*) MATCH_ANY_VALUE, },     /* property disabled */
71     { "GtkColorSelection",      "child",                NULL, },
72     { "GtkColorSelection",      "current-color",        (void*) NULL, },                /* not a valid boxed color */
73     { "GtkComboBox",            "row-span-column",      (void*) MATCH_ANY_VALUE },      /* GtkComboBoxEntry needs a tree model for this */
74     { "GtkComboBox",            "column-span-column",   (void*) MATCH_ANY_VALUE },      /* GtkComboBoxEntry needs a tree model for this */
75     { "GtkComboBoxEntry",       "text-column",          (void*) MATCH_ANY_VALUE },      /* GtkComboBoxEntry needs a tree model for this */
76     { "GtkFileChooserButton",   "select-multiple",      (void*) MATCH_ANY_VALUE },      /* property disabled */
77     { "GtkFileChooserButton",   "action",               (void*) GTK_FILE_CHOOSER_ACTION_SAVE },
78     { "GtkFileChooserButton",   "action",               (void*) GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER },
79     { "GtkFileChooserWidget",   "select-multiple",      (void*) 0x1 },                  /* property conflicts */
80     { "GtkFileChooserDialog",   "select-multiple",      (void*) MATCH_ANY_VALUE },      /* property disabled */
81     { "GtkMenu",                "accel-path",           (void*) MATCH_ANY_VALUE },      /* has odd restrictions in the setter */
82     { "GtkMenuItem",            "accel-path",           (void*) MATCH_ANY_VALUE },      /* has odd restrictions in the setter */
83     { "GtkRecentChooserMenu",   "select-multiple",      (void*) MATCH_ANY_VALUE },      /* property disabled */
84     { "GtkTextView",            "overwrite",            (void*) MATCH_ANY_VALUE },      /* needs text buffer */
85     { "GtkToolbar",             "icon-size",            (void*) GTK_ICON_SIZE_INVALID },
86     { NULL, NULL, NULL }
87   };
88   /* properties suspected to be Gdk/Gtk+ bugs */
89   static const IgnoreProperty bug_properties[] = {
90     { "GtkMessageDialog",       "image",                NULL, },                        /* FIXME: should accept NULL images */
91     { "GtkOptionMenu",          "menu",                 NULL, },                        /* FIXME: should accept NULL menus */
92     { "GtkComboBox",            "active",               (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL model bug */
93     { "GtkComboBoxEntry",       "text-column",          (void*) 0xffffffff },           /* FIXME: triggers signedness bug */
94     { "GtkCTree",               "indent",               (void*) MATCH_ANY_VALUE },      /* FIXME: triggers signedness bug */
95     { "GtkCTree",               "spacing",              (void*) MATCH_ANY_VALUE },      /* FIXME: triggers signedness bug */
96     { "GtkCurve",               "curve-type",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers OOM */
97     { "GtkCurve",               "min-x",                (void*) 0x80000000 },           /* FIXME: triggers coordinate OOB */
98     { "GtkCurve",               "min-y",                (void*) 0x80000000 },           /* FIXME: triggers coordinate OOB */
99     { "GtkCurve",               "max-x",                (void*) 0x80000000 },           /* FIXME: triggers coordinate OOB */
100     { "GtkCurve",               "max-y",                (void*) 0x80000000 },           /* FIXME: triggers coordinate OOB */
101     { "GtkFileChooserButton",   "local-only",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL path assertion */
102     { "GtkFileChooserDialog",   "local-only",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL path assertion */
103     { "GtkFileChooserDialog",   "action",               (void*) MATCH_ANY_VALUE },      /* FIXME: triggers closure->ref_count assertion */
104     { "GtkFileChooserDialog",   "visible",              (void*) TRUE },                 /* FIXME: triggers gtk_window_resize assertion */
105     { "GtkFileChooserWidget",   "local-only",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL path assertion */
106     { "GtkFontSelection",       "font-name",            (void*) MATCH_ANY_VALUE },      /* FIXME: requires non-NULL GdkScreen */
107     { "GtkInvisible",           "has-focus",            (void*) TRUE },                 /* FIXME: triggers invalid window cast */
108     { "GtkInvisible",           "is-focus",             (void*) TRUE },                 /* FIXME: triggers invalid window cast */
109     { "GtkMenu",                "tearoff-state",        (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL widget cast */
110     { "GtkProgress",            "activity-mode",        (void*) TRUE },                 /* FIXME: segfaults */
111     { "GtkScaleButton",         "adjustment",           NULL, },                        /* FIXME: should accept NULL adjustments */
112     { "GtkStatusbar",           "sensitive",            (void*) FALSE },                /* FIXME: check if widget is realize */
113     { "GtkTable",               "n-rows",               (void*) MATCH_ANY_VALUE },      /* FIXME: fix property minimum/maximum */
114     { "GtkTable",               "n-columns",            (void*) MATCH_ANY_VALUE },      /* FIXME: fix property minimum/maximum */
115     { "GtkText",                "text-position",        (void*) MATCH_ANY_VALUE },      /* FIXME: segfaults, fix property minimum/maximum */
116     { NULL, NULL, NULL }
117   };
118   if (buglist)
119     return bug_properties;
120   else
121     return ignore_properties;
122 }
123
124 /* --- test functions --- */
125 static void
126 pspec_select_value (GParamSpec *pspec,
127                     GValue     *value,
128                     double      dvalue)
129 {
130   /* generate a value suitable for pspec */
131   if (G_IS_PARAM_SPEC_CHAR (pspec))
132     ASSIGN_VALUE (g_value_set_char, value, GParamSpecChar*, pspec, default_value, minimum, maximum, dvalue);
133   else if (G_IS_PARAM_SPEC_UCHAR (pspec))
134     ASSIGN_VALUE (g_value_set_uchar, value, GParamSpecUChar*, pspec, default_value, minimum, maximum, dvalue);
135   else if (G_IS_PARAM_SPEC_INT (pspec))
136     ASSIGN_VALUE (g_value_set_int, value, GParamSpecInt*, pspec, default_value, minimum, maximum, dvalue);
137   else if (G_IS_PARAM_SPEC_UINT (pspec))
138     ASSIGN_VALUE (g_value_set_uint, value, GParamSpecUInt*, pspec, default_value, minimum, maximum, dvalue);
139   else if (G_IS_PARAM_SPEC_LONG (pspec))
140     ASSIGN_VALUE (g_value_set_long, value, GParamSpecLong*, pspec, default_value, minimum, maximum, dvalue);
141   else if (G_IS_PARAM_SPEC_ULONG (pspec))
142     ASSIGN_VALUE (g_value_set_ulong, value, GParamSpecULong*, pspec, default_value, minimum, maximum, dvalue);
143   else if (G_IS_PARAM_SPEC_INT64 (pspec))
144     ASSIGN_VALUE (g_value_set_int64, value, GParamSpecInt64*, pspec, default_value, minimum, maximum, dvalue);
145   else if (G_IS_PARAM_SPEC_UINT64 (pspec))
146     ASSIGN_VALUE (g_value_set_uint64, value, GParamSpecUInt64*, pspec, default_value, minimum, maximum, dvalue);
147   else if (G_IS_PARAM_SPEC_FLOAT (pspec))
148     ASSIGN_VALUE (g_value_set_float, value, GParamSpecFloat*, pspec, default_value, minimum, maximum, dvalue);
149   else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
150     ASSIGN_VALUE (g_value_set_double, value, GParamSpecDouble*, pspec, default_value, minimum, maximum, dvalue);
151   else if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
152     g_value_set_boolean (value, SELECT_VALUE (dvalue, ((GParamSpecBoolean*) pspec)->default_value, FALSE, TRUE));
153   else if (G_IS_PARAM_SPEC_UNICHAR (pspec))
154     g_value_set_uint (value, SELECT_VALUE (dvalue, ((GParamSpecUnichar*) pspec)->default_value, FALSE, TRUE));
155   else if (G_IS_PARAM_SPEC_GTYPE (pspec))
156     g_value_set_gtype (value, SELECT_VALUE ((int) dvalue, ((GParamSpecGType*) pspec)->is_a_type, 0, GTK_TYPE_WIDGET));
157   else if (G_IS_PARAM_SPEC_STRING (pspec))
158     {
159       GParamSpecString *sspec = (GParamSpecString*) pspec;
160       if (dvalue >= +2)
161         g_value_set_string (value, sspec->default_value);
162       if (dvalue > 0 && sspec->cset_first && sspec->cset_nth)
163         g_value_take_string (value, g_strdup_printf ("%c%c", sspec->cset_first[0], sspec->cset_nth[0]));
164       else /* if (sspec->ensure_non_null) */
165         g_value_set_string (value, "");
166     }
167   else if (G_IS_PARAM_SPEC_ENUM (pspec))
168     {
169       GParamSpecEnum *espec = (GParamSpecEnum*) pspec;
170       if (dvalue >= +2)
171         g_value_set_enum (value, espec->default_value);
172       if (dvalue >= 0 && dvalue <= 1)
173         g_value_set_enum (value, espec->enum_class->values[(int) ((espec->enum_class->n_values - 1) * dvalue)].value);
174       else if (dvalue <= -1)
175         g_value_set_enum (value, espec->enum_class->values[g_test_rand_int_range (0, espec->enum_class->n_values)].value);
176     }
177   else if (G_IS_PARAM_SPEC_FLAGS (pspec))
178     {
179       GParamSpecFlags *fspec = (GParamSpecFlags*) pspec;
180       if (dvalue >= +2)
181         g_value_set_flags (value, fspec->default_value);
182       if (dvalue >= 0 && dvalue <= 1)
183         g_value_set_flags (value, fspec->flags_class->values[(int) ((fspec->flags_class->n_values - 1) * dvalue)].value);
184       else if (dvalue <= -1)
185         g_value_set_flags (value, fspec->flags_class->values[g_test_rand_int_range (0, fspec->flags_class->n_values)].value);
186     }
187   /* unimplemented:
188    * G_IS_PARAM_SPEC_PARAM
189    * G_IS_PARAM_SPEC_BOXED
190    * G_IS_PARAM_SPEC_POINTER
191    * G_IS_PARAM_SPEC_VALUE_ARRAY
192    * G_IS_PARAM_SPEC_OBJECT
193    */
194 }
195
196 static gpointer
197 value_as_pointer (GValue *value)
198 {
199   if (g_value_fits_pointer (value))
200     return g_value_peek_pointer (value);
201   if (G_VALUE_HOLDS_BOOLEAN (value))
202     return (void*) g_value_get_boolean (value);
203   if (G_VALUE_HOLDS_CHAR (value))
204     return (void*) (gssize) g_value_get_char (value);
205   if (G_VALUE_HOLDS_UCHAR (value))
206     return (void*) (gsize) g_value_get_uchar (value);
207   if (G_VALUE_HOLDS_INT (value))
208     return (void*) g_value_get_int (value);
209   if (G_VALUE_HOLDS_UINT (value))
210     return (void*) g_value_get_uint (value);
211   if (G_VALUE_HOLDS_LONG (value))
212     return (void*) g_value_get_long (value);
213   if (G_VALUE_HOLDS_ULONG (value))
214     return (void*) g_value_get_ulong (value);
215   if (G_VALUE_HOLDS_FLOAT (value))
216     return (void*) (gssize) g_value_get_float (value);
217   if (G_VALUE_HOLDS_DOUBLE (value))
218     return (void*) (gssize) g_value_get_double (value);
219   if (G_VALUE_HOLDS_ENUM (value))
220     return (void*) (gssize) g_value_get_enum (value);
221   if (G_VALUE_HOLDS_FLAGS (value))
222     return (void*) (gsize) g_value_get_flags (value);
223   return (void*) 0x1373babe;
224 }
225
226 static void
227 object_test_property (GObject           *object,
228                       GParamSpec        *pspec,
229                       double             dvalue)
230 {
231   /* test setting of a normal writable property */
232   if (pspec->flags & G_PARAM_WRITABLE &&
233       !(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
234     {
235       GValue value = { 0, };
236       guint i;
237       const IgnoreProperty *ignore_properties;
238       /* select value to set */
239       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
240       pspec_select_value (pspec, &value, dvalue);
241       /* ignore untestable properties */
242       ignore_properties = list_ignore_properties (FALSE);
243       for (i = 0; ignore_properties[i].name; i++)
244         if (g_type_is_a (G_OBJECT_TYPE (object), g_type_from_name (ignore_properties[i].type_name)) &&
245             strcmp (pspec->name, ignore_properties[i].name) == 0 &&
246             (MATCH_ANY_VALUE == ignore_properties[i].value ||
247              value_as_pointer (&value) == ignore_properties[i].value ||
248              (G_VALUE_HOLDS_STRING (&value) &&
249               strcmp (g_value_get_string (&value), ignore_properties[i].value) == 0)))
250           break;
251       /* ignore known property bugs if not testing thoroughly */
252       if (ignore_properties[i].name == NULL && !g_test_thorough())
253         {
254           ignore_properties = list_ignore_properties (TRUE);
255           for (i = 0; ignore_properties[i].name; i++)
256             if (g_type_is_a (G_OBJECT_TYPE (object), g_type_from_name (ignore_properties[i].type_name)) &&
257                 strcmp (pspec->name, ignore_properties[i].name) == 0 &&
258                 (MATCH_ANY_VALUE == ignore_properties[i].value ||
259                  value_as_pointer (&value) == ignore_properties[i].value ||
260                  (G_VALUE_HOLDS_STRING (&value) &&
261                   strcmp (g_value_get_string (&value), ignore_properties[i].value) == 0)))
262               break;
263         }
264       /* assign unignored properties */
265       if (ignore_properties[i].name == NULL)
266         {
267           if (g_test_verbose())
268             g_print ("PropertyTest: %s::%s := (%s value (%s): %p)\n",
269                      g_type_name (G_OBJECT_TYPE (object)), pspec->name,
270                      SELECT_NAME (dvalue), g_type_name (G_VALUE_TYPE (&value)),
271                      value_as_pointer (&value));
272           g_object_set_property (object, pspec->name, &value);
273         }
274       /* cleanups */
275       g_value_unset (&value);
276     }
277 }
278
279 static void
280 widget_test_properties (GtkWidget   *widget,
281                         double       dvalue)
282 {
283   /* try setting all possible properties, according to dvalue */
284   guint i, n_pspecs = 0;
285   GParamSpec **pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (widget), &n_pspecs);
286   for (i = 0; i < n_pspecs; i++)
287     {
288       GParamSpec *pspec = pspecs[i];
289       if (pspec->flags & G_PARAM_WRITABLE &&
290           !(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
291         object_test_property (G_OBJECT (widget), pspecs[i], dvalue);
292     }
293   g_free (pspecs);
294 }
295
296 static void
297 widget_fixups (GtkWidget *widget)
298 {
299   /* post-constructor for widgets that need additional settings to work correctly */
300   if (GTK_IS_COMBO_BOX_ENTRY (widget))
301     {
302       GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
303       g_object_set (widget, "model", store, "text-column", 0, NULL);
304       g_object_unref (store);
305       gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "test text");
306     }
307   else if (GTK_IS_COMBO_BOX (widget))
308     {
309       GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING);
310       g_object_set (widget, "model", store, NULL);
311       g_object_unref (store);
312       gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "test text");
313     }
314 }
315
316 static void
317 widget_property_tests (gconstpointer test_data)
318 {
319   GType wtype = (GType) test_data;
320   /* create widget */
321   GtkWidget *widget = gtk_widget_new (wtype, NULL);
322   g_object_ref_sink (widget);
323   widget_fixups (widget);
324   /* test property values */
325   widget_test_properties (widget,  +2); /* test default_value */
326   widget_test_properties (widget,   0); /* test minimum */
327   widget_test_properties (widget, 0.5); /* test medium */
328   widget_test_properties (widget,   1); /* test maximum */
329   widget_test_properties (widget,  -1); /* test random value */
330   /* cleanup */
331   gtk_widget_destroy (widget);
332   g_object_unref (widget);
333 }
334
335 extern void pixbuf_init (void);
336
337 /* --- main test program --- */
338 int
339 main (int   argc,
340       char *argv[])
341 {
342   const GType *otypes;
343   guint i;
344   /* initialize test program */
345   pixbuf_init ();
346   gtk_test_init (&argc, &argv);
347   gtk_test_register_all_types();
348   /* install a property test for each widget type */
349   otypes = gtk_test_list_all_types (NULL);
350   for (i = 0; otypes[i]; i++)
351     if (g_type_is_a (otypes[i], GTK_TYPE_WIDGET) &&
352         G_TYPE_IS_OBJECT (otypes[i]) &&
353         !G_TYPE_IS_ABSTRACT (otypes[i]))
354       {
355         gchar *testpath = g_strdup_printf ("/properties/%s", g_type_name (otypes[i]));
356         g_test_add_data_func (testpath, (void*) otypes[i], widget_property_tests);
357         g_free (testpath);
358       }
359   return g_test_run();
360 }