2 * Copyright (C) 2007 Imendio AB
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.
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.
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.
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
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)); \
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) : \
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" : \
45 #define MATCH_ANY_VALUE ((void*) 0xf1874c23)
47 /* --- property blacklists --- */
49 const char *type_name;
53 static const IgnoreProperty*
54 list_ignore_properties (gboolean buglist)
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 },
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 */
119 return bug_properties;
121 return ignore_properties;
124 /* --- test functions --- */
126 pspec_select_value (GParamSpec *pspec,
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))
159 GParamSpecString *sspec = (GParamSpecString*) pspec;
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, "");
167 else if (G_IS_PARAM_SPEC_ENUM (pspec))
169 GParamSpecEnum *espec = (GParamSpecEnum*) pspec;
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);
177 else if (G_IS_PARAM_SPEC_FLAGS (pspec))
179 GParamSpecFlags *fspec = (GParamSpecFlags*) pspec;
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);
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
197 value_as_pointer (GValue *value)
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;
227 object_test_property (GObject *object,
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)))
235 GValue value = { 0, };
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)))
251 /* ignore known property bugs if not testing thoroughly */
252 if (ignore_properties[i].name == NULL && !g_test_thorough())
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)))
264 /* assign unignored properties */
265 if (ignore_properties[i].name == NULL)
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);
275 g_value_unset (&value);
280 widget_test_properties (GtkWidget *widget,
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++)
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);
297 widget_fixups (GtkWidget *widget)
299 /* post-constructor for widgets that need additional settings to work correctly */
300 if (GTK_IS_COMBO_BOX_ENTRY (widget))
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");
307 else if (GTK_IS_COMBO_BOX (widget))
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");
317 widget_property_tests (gconstpointer test_data)
319 GType wtype = (GType) test_data;
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 */
331 gtk_widget_destroy (widget);
332 g_object_unref (widget);
335 extern void pixbuf_init (void);
337 /* --- main test program --- */
344 /* initialize test program */
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]))
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);