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, see <http://www.gnu.org/licenses/>.
17 */
18#include <gtk/gtk.h>
19#include <string.h>
20
21/* --- helper macros for property value generation --- */
22/* dvalue=+0: generate minimum value
23 * dvalue=.x: generate value within value range proportional to x.
24 * dvalue=+1: generate maximum value
25 * dvalue=-1: generate random value within value range
26 * dvalue=+2: initialize value from default_value
27 */
28#define ASSIGN_VALUE(__g_value_set_func, value__, PSPECTYPE, __pspec, __default_value, __minimum, __maximum, __dvalue) do { \
29 PSPECTYPE __p = (PSPECTYPE) __pspec; \
30 __g_value_set_func (value__, SELECT_VALUE (__dvalue, __p->__default_value, __p->__minimum, __p->__maximum)); \
31} while (0)
32#define SELECT_VALUE(__dvalue, __default_value, __minimum, __maximum) ( \
33 __dvalue >= 0 && __dvalue <= 1 ? __minimum * (1 - __dvalue) + __dvalue * __maximum : \
34 __dvalue <= -1 ? g_test_rand_double_range (__minimum, __maximum) : \
35 __default_value)
36#define SELECT_NAME(__dvalue) ( \
37 __dvalue == 0 ? "minimum" : \
38 __dvalue == 1 ? "maximum" : \
39 __dvalue >= +2 ? "default" : \
40 __dvalue == 0.5 ? "medium" : \
41 __dvalue > 0 && __dvalue < 1 ? "fractional" : \
42 "random")
43#define MATCH_ANY_VALUE ((void*) 0xf1874c23)
44
45/* --- ignored property names --- */
46typedef struct {
47 const char *type_name;
48 const char *name;
49 gconstpointer value;
50} IgnoreProperty;
51static const IgnoreProperty*
52list_ignore_properties (gboolean buglist)
53{
54 /* currently untestable properties */
55 static const IgnoreProperty ignore_properties[] = {
56 { "GtkWidget", "parent", NULL, }, /* needs working parent widget */
57 { "GtkWidget", "has-default", (void*) TRUE, }, /* conflicts with toplevel-less widgets */
58 { "GtkWidget", "display", (void*) MATCH_ANY_VALUE },
59 { "GtkCellView", "background", (void*) "", }, /* "" is not a valid background color */
60 { "GtkFileChooserWidget", "select-multiple", (void*) 0x1 }, /* property conflicts */
61 { "GtkFileChooserDialog", "select-multiple", (void*) MATCH_ANY_VALUE }, /* property disabled */
62 { "GtkTextView", "overwrite", (void*) MATCH_ANY_VALUE }, /* needs text buffer */
63 { "GtkTreeView", "expander-column", (void*) MATCH_ANY_VALUE }, /* assertion list != NULL */
64 { "GtkWindow", "display", (void*) MATCH_ANY_VALUE },
65 { NULL, NULL, NULL }
66 };
67 /* properties suspected to be Gdk/Gtk+ bugs */
68 static const IgnoreProperty bug_properties[] = {
69 { "GtkComboBox", "active", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers NULL model bug */
70 { NULL, NULL, NULL }
71 };
72 if (buglist)
73 return bug_properties;
74 else
75 return ignore_properties;
76}
77
78/* --- test functions --- */
79static void
80pspec_select_value (GParamSpec *pspec,
81 GValue *value,
82 double dvalue)
83{
84 /* generate a value suitable for pspec */
85 if (G_IS_PARAM_SPEC_CHAR (pspec))
86 ASSIGN_VALUE (g_value_set_schar, value, GParamSpecChar*, pspec, default_value, minimum, maximum, dvalue);
87 else if (G_IS_PARAM_SPEC_UCHAR (pspec))
88 ASSIGN_VALUE (g_value_set_uchar, value, GParamSpecUChar*, pspec, default_value, minimum, maximum, dvalue);
89 else if (G_IS_PARAM_SPEC_INT (pspec))
90 ASSIGN_VALUE (g_value_set_int, value, GParamSpecInt*, pspec, default_value, minimum, maximum, dvalue);
91 else if (G_IS_PARAM_SPEC_UINT (pspec))
92 ASSIGN_VALUE (g_value_set_uint, value, GParamSpecUInt*, pspec, default_value, minimum, maximum, dvalue);
93 else if (G_IS_PARAM_SPEC_LONG (pspec))
94 ASSIGN_VALUE (g_value_set_long, value, GParamSpecLong*, pspec, default_value, minimum, maximum, dvalue);
95 else if (G_IS_PARAM_SPEC_ULONG (pspec))
96 ASSIGN_VALUE (g_value_set_ulong, value, GParamSpecULong*, pspec, default_value, minimum, maximum, dvalue);
97 else if (G_IS_PARAM_SPEC_INT64 (pspec))
98 ASSIGN_VALUE (g_value_set_int64, value, GParamSpecInt64*, pspec, default_value, minimum, maximum, dvalue);
99 else if (G_IS_PARAM_SPEC_UINT64 (pspec))
100 ASSIGN_VALUE (g_value_set_uint64, value, GParamSpecUInt64*, pspec, default_value, minimum, maximum, dvalue);
101 else if (G_IS_PARAM_SPEC_FLOAT (pspec))
102 ASSIGN_VALUE (g_value_set_float, value, GParamSpecFloat*, pspec, default_value, minimum, maximum, dvalue);
103 else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
104 ASSIGN_VALUE (g_value_set_double, value, GParamSpecDouble*, pspec, default_value, minimum, maximum, dvalue);
105 else if (G_IS_PARAM_SPEC_BOOLEAN (pspec))
106 g_value_set_boolean (value, SELECT_VALUE (dvalue, ((GParamSpecBoolean*) pspec)->default_value, FALSE, TRUE));
107 else if (G_IS_PARAM_SPEC_UNICHAR (pspec))
108 g_value_set_uint (value, SELECT_VALUE (dvalue, ((GParamSpecUnichar*) pspec)->default_value, FALSE, TRUE));
109 else if (G_IS_PARAM_SPEC_GTYPE (pspec))
110 g_value_set_gtype (value, SELECT_VALUE ((int) dvalue, ((GParamSpecGType*) pspec)->is_a_type, 0, GTK_TYPE_WIDGET));
111 else if (G_IS_PARAM_SPEC_STRING (pspec))
112 {
113 GParamSpecString *sspec = (GParamSpecString*) pspec;
114 if (dvalue >= +2)
115 g_value_set_string (value, v_string: sspec->default_value);
116 if (dvalue > 0 && sspec->cset_first && sspec->cset_nth)
117 g_value_take_string (value, v_string: g_strdup_printf (format: "%c%c", sspec->cset_first[0], sspec->cset_nth[0]));
118 else /* if (sspec->ensure_non_null) */
119 g_value_set_string (value, v_string: "");
120 }
121 else if (G_IS_PARAM_SPEC_ENUM (pspec))
122 {
123 GParamSpecEnum *espec = (GParamSpecEnum*) pspec;
124 if (dvalue >= +2)
125 g_value_set_enum (value, v_enum: espec->default_value);
126 if (dvalue >= 0 && dvalue <= 1)
127 g_value_set_enum (value, v_enum: espec->enum_class->values[(int) ((espec->enum_class->n_values - 1) * dvalue)].value);
128 else if (dvalue <= -1)
129 g_value_set_enum (value, v_enum: espec->enum_class->values[g_test_rand_int_range (begin: 0, end: espec->enum_class->n_values)].value);
130 }
131 else if (G_IS_PARAM_SPEC_FLAGS (pspec))
132 {
133 GParamSpecFlags *fspec = (GParamSpecFlags*) pspec;
134 if (dvalue >= +2)
135 g_value_set_flags (value, v_flags: fspec->default_value);
136 if (dvalue >= 0 && dvalue <= 1)
137 g_value_set_flags (value, v_flags: fspec->flags_class->values[(int) ((fspec->flags_class->n_values - 1) * dvalue)].value);
138 else if (dvalue <= -1)
139 g_value_set_flags (value, v_flags: fspec->flags_class->values[g_test_rand_int_range (begin: 0, end: fspec->flags_class->n_values)].value);
140 }
141 else if (G_IS_PARAM_SPEC_OBJECT (pspec))
142 {
143 gpointer object = NULL;
144 if (!G_TYPE_IS_ABSTRACT (pspec->value_type) &&
145 !G_TYPE_IS_INTERFACE (pspec->value_type))
146 {
147 if (g_type_is_a (type: pspec->value_type, GDK_TYPE_PIXBUF))
148 object = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, FALSE, bits_per_sample: 8, width: 32, height: 32);
149 else if (g_type_is_a (type: pspec->value_type, GDK_TYPE_PIXBUF_ANIMATION))
150 object = gdk_pixbuf_simple_anim_new (width: 32, height: 32, rate: 15);
151 else
152 object = g_object_new (object_type: pspec->value_type, NULL);
153 g_object_ref_sink (object);
154 g_value_take_object (value, v_object: object);
155 }
156 }
157 /* unimplemented:
158 * G_IS_PARAM_SPEC_PARAM
159 * G_IS_PARAM_SPEC_BOXED
160 * G_IS_PARAM_SPEC_POINTER
161 * G_IS_PARAM_SPEC_VALUE_ARRAY
162 */
163}
164
165static gpointer
166value_as_pointer (GValue *value)
167{
168 if (g_value_fits_pointer (value))
169 return g_value_peek_pointer (value);
170 if (G_VALUE_HOLDS_BOOLEAN (value))
171 return GINT_TO_POINTER(g_value_get_boolean (value));
172 if (G_VALUE_HOLDS_CHAR (value))
173 return (void*) (gssize) g_value_get_schar (value);
174 if (G_VALUE_HOLDS_UCHAR (value))
175 return (void*) (gsize) g_value_get_uchar (value);
176 if (G_VALUE_HOLDS_INT (value))
177 return GINT_TO_POINTER(g_value_get_int (value));
178 if (G_VALUE_HOLDS_UINT (value))
179 return GUINT_TO_POINTER(g_value_get_uint (value));
180 if (G_VALUE_HOLDS_LONG (value))
181 return GSIZE_TO_POINTER ((gssize) g_value_get_long (value));
182 if (G_VALUE_HOLDS_ULONG (value))
183 return GSIZE_TO_POINTER (g_value_get_ulong (value));
184 if (G_VALUE_HOLDS_FLOAT (value))
185 return (void*) (gssize) g_value_get_float (value);
186 if (G_VALUE_HOLDS_DOUBLE (value))
187 return (void*) (gssize) g_value_get_double (value);
188 if (G_VALUE_HOLDS_ENUM (value))
189 return (void*) (gssize) g_value_get_enum (value);
190 if (G_VALUE_HOLDS_FLAGS (value))
191 return (void*) (gsize) g_value_get_flags (value);
192 return (void*) 0x1373babe;
193}
194
195static void
196object_test_property (GObject *object,
197 GParamSpec *pspec,
198 double dvalue)
199{
200 /* test setting of a normal writable property */
201 if (pspec->flags & G_PARAM_WRITABLE &&
202 !(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
203 {
204 GValue value = G_VALUE_INIT;
205 guint i;
206 const IgnoreProperty *ignore_properties;
207 /* select value to set */
208 g_value_init (value: &value, G_PARAM_SPEC_VALUE_TYPE (pspec));
209 pspec_select_value (pspec, value: &value, dvalue);
210 /* ignore untestable properties */
211 ignore_properties = list_ignore_properties (FALSE);
212 for (i = 0; ignore_properties[i].name; i++)
213 if (g_strcmp0 (str1: "", str2: ignore_properties[i].name) ||
214 (g_type_is_a (G_OBJECT_TYPE (object), is_a_type: g_type_from_name (name: ignore_properties[i].type_name)) &&
215 strcmp (s1: pspec->name, s2: ignore_properties[i].name) == 0 &&
216 (MATCH_ANY_VALUE == ignore_properties[i].value ||
217 value_as_pointer (value: &value) == ignore_properties[i].value ||
218 (G_VALUE_HOLDS_STRING (&value) &&
219 g_strcmp0 (str1: g_value_get_string (value: &value), str2: ignore_properties[i].value) == 0))))
220 break;
221 /* ignore known property bugs if not testing thoroughly */
222 if (ignore_properties[i].name == NULL && !g_test_thorough ())
223 {
224 ignore_properties = list_ignore_properties (TRUE);
225 for (i = 0; ignore_properties[i].name; i++)
226 if (g_type_is_a (G_OBJECT_TYPE (object), is_a_type: g_type_from_name (name: ignore_properties[i].type_name)) &&
227 strcmp (s1: pspec->name, s2: ignore_properties[i].name) == 0 &&
228 (MATCH_ANY_VALUE == ignore_properties[i].value ||
229 value_as_pointer (value: &value) == ignore_properties[i].value ||
230 (G_VALUE_HOLDS_STRING (&value) &&
231 g_strcmp0 (str1: g_value_get_string (value: &value), str2: ignore_properties[i].value) == 0)))
232 break;
233 }
234 /* assign unignored properties */
235 if (ignore_properties[i].name == NULL)
236 {
237 if (g_test_verbose ())
238 g_print (format: "PropertyTest: %s::%s := (%s value (%s): %p)\n",
239 g_type_name (G_OBJECT_TYPE (object)), pspec->name,
240 SELECT_NAME (dvalue), g_type_name (G_VALUE_TYPE (&value)),
241 value_as_pointer (value: &value));
242 g_object_set_property (object, property_name: pspec->name, value: &value);
243 }
244 /* cleanups */
245 g_value_unset (value: &value);
246 }
247}
248
249static void
250widget_test_properties (GtkWidget *widget,
251 double dvalue)
252{
253 /* try setting all possible properties, according to dvalue */
254 guint i, n_pspecs = 0;
255 GParamSpec **pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (widget), n_properties: &n_pspecs);
256 for (i = 0; i < n_pspecs; i++)
257 {
258 GParamSpec *pspec = pspecs[i];
259 if (pspec->flags & G_PARAM_WRITABLE &&
260 !(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)))
261 object_test_property (G_OBJECT (widget), pspec: pspecs[i], dvalue);
262 }
263 g_free (mem: pspecs);
264}
265
266static void
267widget_fixups (GtkWidget *widget)
268{
269 /* post-constructor for widgets that need additional settings to work correctly */
270 if (GTK_IS_COMBO_BOX_TEXT (widget))
271 {
272 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget), text: "test text");
273 }
274}
275
276static void
277widget_property_tests (gconstpointer test_data)
278{
279 GType wtype = (GType) test_data;
280 /* create widget */
281 GtkWidget *widget = g_object_new (object_type: wtype, NULL);
282 g_object_ref_sink (widget);
283 widget_fixups (widget);
284 /* test property values */
285 widget_test_properties (widget, dvalue: +2); /* test default_value */
286 widget_test_properties (widget, dvalue: 0); /* test minimum */
287 widget_test_properties (widget, dvalue: 0.5); /* test medium */
288 widget_test_properties (widget, dvalue: 1); /* test maximum */
289 widget_test_properties (widget, dvalue: -1); /* test random value */
290 /* cleanup */
291 if (GTK_IS_WINDOW (widget))
292 gtk_window_destroy (GTK_WINDOW (widget));
293 g_object_unref (object: widget);
294}
295
296/* --- main test program --- */
297int
298main (int argc,
299 char *argv[])
300{
301 const GType *otypes;
302 guint i;
303
304 g_setenv (variable: "GSETTINGS_BACKEND", value: "memory", TRUE);
305
306 /* initialize test program */
307 gtk_test_init (argcp: &argc, argvp: &argv);
308 gtk_test_register_all_types ();
309
310 /* install a property test for each widget type */
311 otypes = gtk_test_list_all_types (NULL);
312 for (i = 0; otypes[i]; i++)
313 if (g_type_is_a (type: otypes[i], GTK_TYPE_WIDGET) &&
314 G_TYPE_IS_OBJECT (otypes[i]) &&
315 !G_TYPE_IS_ABSTRACT (otypes[i]))
316 {
317 char *testpath = g_strdup_printf (format: "/properties/%s", g_type_name (type: otypes[i]));
318 g_test_add_data_func (testpath, test_data: (void*) otypes[i], test_func: widget_property_tests);
319 g_free (mem: testpath);
320 }
321
322 return g_test_run ();
323}
324

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