From: 18:59:59 Tim Janik Date: Wed, 5 Dec 2007 16:58:44 +0000 (+0000) Subject: run tests in current dir after setting up the logging directory, so their X-Git-Url: http://pileus.org/git/?p=~andy%2Fgtk;a=commitdiff_plain;h=9ba5da2f3644bdf5494a6b06dce7b6914e44b5af run tests in current dir after setting up the logging directory, so their 2007-12-05 18:59:59 Tim Janik * gtk+/Makefile.decl: run tests in current dir after setting up the logging directory, so their results get properly merged into the resulting test log. * gtk+/gtk/gtktestutils.[hc]: added gtk_test_list_all_types() for tests to loop over registered Gdk/Gtk+ types. * gtk+/tests/objecttests.c: new test program, implements automated property tests. several properties are blacklisted because they seem to trigger Gdk/Gtk+ bugs. ./objecttests -m thorough --verbose can be used to test blacklisted properties and see which proprty failed. svn path=/trunk/; revision=19115 --- diff --git a/ChangeLog b/ChangeLog index 8f59f21c4..5d2b49b30 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-12-05 18:59:59 Tim Janik + + * gtk+/Makefile.decl: run tests in current dir after setting up the + logging directory, so their results get properly merged into the + resulting test log. + + * gtk+/gtk/gtktestutils.[hc]: added gtk_test_list_all_types() for + tests to loop over registered Gdk/Gtk+ types. + + * gtk+/tests/objecttests.c: new test program, implements automated + property tests. several properties are blacklisted because they + seem to trigger Gdk/Gtk+ bugs. ./objecttests -m thorough --verbose + can be used to test blacklisted properties and see which proprty failed. + 2007-12-04 Richard Hult * gdk/quartz/gdkwindow-quartz.c: (_gdk_windowing_window_init): diff --git a/Makefile.decl b/Makefile.decl index 2963f2bd4..4caa19b77 100644 --- a/Makefile.decl +++ b/Makefile.decl @@ -32,20 +32,6 @@ test: ${TEST_PROGS} # perf-report: run tests in subdirs with -m perf and generate report # full-report: like test-report: with -m perf and -m slow test-report perf-report full-report: ${TEST_PROGS} - @test -z "${TEST_PROGS}" || { \ - case $@ in \ - test-report) test_options="-k";; \ - perf-report) test_options="-k -m=perf";; \ - full-report) test_options="-k -m=perf -m=slow";; \ - esac ; \ - $(XVFB_START) && { \ - if test -z "$$GTESTER_LOGDIR" ; then \ - ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ - elif test -n "${TEST_PROGS}" ; then \ - ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ - fi ; \ - } \ - } @ ignore_logdir=true ; \ if test -z "$$GTESTER_LOGDIR" ; then \ GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \ @@ -55,6 +41,20 @@ test-report perf-report full-report: ${TEST_PROGS} test "$$subdir" = "." -o "$$subdir" = "po" -o "$$subdir" = "po-properties" || \ ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ done ; \ + test -z "${TEST_PROGS}" || { \ + case $@ in \ + test-report) test_options="-k";; \ + perf-report) test_options="-k -m=perf";; \ + full-report) test_options="-k -m=perf -m=slow";; \ + esac ; \ + $(XVFB_START) && { \ + if test -z "$$GTESTER_LOGDIR" ; then \ + ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ + elif test -n "${TEST_PROGS}" ; then \ + ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ + fi ; \ + }; \ + }; \ $$ignore_logdir || { \ echo '' > $@.xml ; \ echo '' >> $@.xml ; \ diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 7e1395365..359d1c9cd 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -817,7 +817,7 @@ gtktypefuncs.c: @REBUILD@ $(top_srcdir)/gtk/*.h $(top_srcdir)/gdk/*.h Makefile ${CPP} $(DEFS) $(INCLUDES) -DGTK_ENABLE_BROKEN $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) xgen-gtfsrc.c | \ grep -o '\bg[td]k_[a-zA-Z0-9_]*_get_type\b' | \ sort | uniq | \ - sed '{ s/^/vgt=/; s/$$/();/; }' > xgen-gtf \ + sed '{ s/^/*tp++ = /; s/$$/();/; }' > xgen-gtf \ && cp xgen-gtf $@ && rm -f xgen-gtf gtktestutils.c: gtktypefuncs.c diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index c657cc820..f24330164 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -3263,6 +3263,7 @@ gtk_test_find_sibling gtk_test_find_widget gtk_test_init gtk_test_register_all_types +gtk_test_list_all_types gtk_test_slider_get_value gtk_test_slider_set_perc gtk_test_spin_button_click diff --git a/gtk/gtktestutils.c b/gtk/gtktestutils.c index 001a6a8e7..cafc65723 100644 --- a/gtk/gtktestutils.c +++ b/gtk/gtktestutils.c @@ -60,7 +60,6 @@ gtk_test_init (int *argcp, * FUTURE TODO: * - this function could install a mock object around GtkSettings */ - // FIXME: g_test_init (argcp, argvp, NULL); g_setenv ("GTK_MODULES", "", TRUE); g_setenv ("GTK2_RC_FILES", "/dev/null", TRUE); gtk_disable_setlocale(); @@ -570,6 +569,25 @@ gtk_test_create_simple_window (const gchar *window_title, return window; } +static GType *all_registered_types = NULL; +static guint n_all_registered_types = 0; + +/** + * gtk_test_list_all_types + * @n_types: location to store number of types + * @returns: 0-terminated array of type ids + * + * Return the type ids that have been registered after + * calling gtk_test_register_all_types(). + **/ +const GType* +gtk_test_list_all_types (guint *n_types) +{ + if (n_types) + *n_types = n_all_registered_types; + return all_registered_types; +} + /** * gtk_test_register_all_types * @@ -580,8 +598,17 @@ gtk_test_create_simple_window (const gchar *window_title, void gtk_test_register_all_types (void) { - volatile GType vgt; + if (!all_registered_types) + { + const guint max_gtk_types = 999; + GType *tp; + all_registered_types = g_new0 (GType, max_gtk_types); + tp = all_registered_types; #include "gtktypefuncs.c" + n_all_registered_types = tp - all_registered_types; + g_assert (n_all_registered_types + 1 < max_gtk_types); + *tp = 0; + } } #define __GTK_TEST_UTILS_C__ diff --git a/gtk/gtktestutils.h b/gtk/gtktestutils.h index 412ca8130..bcb2c12fa 100644 --- a/gtk/gtktestutils.h +++ b/gtk/gtktestutils.h @@ -30,6 +30,7 @@ void gtk_test_init (int *argcp, char ***argvp, ...); void gtk_test_register_all_types (void); +const GType* gtk_test_list_all_types (guint *n_types); GtkWidget* gtk_test_find_widget (GtkWidget *widget, const gchar *label_pattern, GType widget_type); diff --git a/tests/Makefile.am b/tests/Makefile.am index d70432141..7f41e2eef 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,9 +25,16 @@ if USE_X11 testsocket_programs = testsocket testsocket_child endif + +TEST_PROGS += objecttests +objecttests_SOURCES = objecttests.c +objecttests_LDADD = $(LDADDS) +objecttests_DEPENDENCIES = $(TEST_DEPS) + + TESTS = floatingtest buildertest -noinst_PROGRAMS = \ +noinst_PROGRAMS = $(TEST_PROGS) \ autotestfilechooser \ autotestkeywords \ floatingtest \ diff --git a/tests/objecttests.c b/tests/objecttests.c new file mode 100644 index 000000000..62d5e1401 --- /dev/null +++ b/tests/objecttests.c @@ -0,0 +1,356 @@ +/* Gtk+ object tests + * Copyright (C) 2007 Imendio AB + * Authors: Tim Janik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include +#include + +/* --- helper macros for property value generation --- */ +/* dvalue=+0: generate minimum value + * dvalue=.x: generate value within value range proportional to x. + * dvalue=+1: generate maximum value + * dvalue=-1: generate random value within value range + * dvalue=+2: initialize value from default_value + */ +#define ASSIGN_VALUE(__g_value_set_func, __value, PSPECTYPE, __pspec, __default_value, __minimum, __maximum, __dvalue) do { \ + PSPECTYPE __p = (PSPECTYPE) __pspec; \ + __g_value_set_func (__value, SELECT_VALUE (__dvalue, __p->__default_value, __p->__minimum, __p->__maximum)); \ +} while (0) +#define SELECT_VALUE(__dvalue, __default_value, __minimum, __maximum) ( \ + __dvalue >= 0 && __dvalue <= 1 ? __minimum * (1 - __dvalue) + __dvalue * __maximum : \ + __dvalue <= -1 ? g_test_rand_double_range (__minimum, __maximum) : \ + __default_value) +#define SELECT_NAME(__dvalue) ( \ + __dvalue == 0 ? "minimum" : \ + __dvalue == 1 ? "maximum" : \ + __dvalue >= +2 ? "default" : \ + __dvalue == 0.5 ? "medium" : \ + __dvalue > 0 && __dvalue < 1 ? "fractional" : \ + "random") +#define MATCH_ANY_VALUE ((void*) 0xf1874c23) + +/* --- variables --- */ +static const char * const the_empty_string = ""; // use this constant to allow pointer comparisons of "" + +/* --- property blacklists --- */ +typedef struct { + const char *type_name; + const char *name; + gconstpointer value; +} IgnoreProperty; +static const IgnoreProperty* +list_ignore_properties (gboolean buglist) +{ + /* currently untestable properties */ + static const IgnoreProperty ignore_properties[] = { + { "GtkContainer", "child", NULL, }, /* needs working child widget */ + { "GtkRadioMenuItem", "group", NULL, }, /* needs working sibling */ + { "GtkWidget", "parent", NULL, }, /* needs working parent widget */ + { "GtkCList", "selection-mode", (void*) GTK_SELECTION_NONE, }, + { "GtkWidget", "has-default", (void*) TRUE, }, /* conflicts with toplevel-less widgets */ + { "GtkWidget", "screen", NULL, }, + { "GtkWindow", "type-hint", (void*) GDK_WINDOW_TYPE_HINT_DND, }, /* conflicts with ::visible=TRUE */ + { "GtkCellView", "background", (void*) the_empty_string, }, /* "" is not a valid background color */ + { "GtkColorButton", "color", (void*) NULL, }, /* not a valid boxed color */ + { "GtkInputDialog", "has-separator", (void*) MATCH_ANY_VALUE, }, /* property disabled */ + { "GtkMessageDialog", "has-separator", (void*) MATCH_ANY_VALUE, }, /* property disabled */ + { "GtkFontSelectionDialog", "has-separator", (void*) MATCH_ANY_VALUE, }, /* property disabled */ + { "GtkColorSelectionDialog","has-separator", (void*) MATCH_ANY_VALUE, }, /* property disabled */ + { "GtkColorSelection", "child", NULL, }, + { "GtkColorSelection", "current-color", (void*) NULL, }, /* not a valid boxed color */ + { "GtkComboBox", "row-span-column", (void*) MATCH_ANY_VALUE }, /* GtkComboBoxEntry needs a tree model for this */ + { "GtkComboBox", "column-span-column", (void*) MATCH_ANY_VALUE }, /* GtkComboBoxEntry needs a tree model for this */ + { "GtkComboBoxEntry", "text-column", (void*) MATCH_ANY_VALUE }, /* GtkComboBoxEntry needs a tree model for this */ + { "GtkFileChooserButton", "select-multiple", (void*) MATCH_ANY_VALUE }, /* property disabled */ + { "GtkFileChooserButton", "action", (void*) GTK_FILE_CHOOSER_ACTION_SAVE }, + { "GtkFileChooserButton", "action", (void*) GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER }, + { "GtkFileChooserWidget", "select-multiple", (void*) 0x1 }, /* property conflicts */ + { "GtkFileChooserDialog", "select-multiple", (void*) MATCH_ANY_VALUE }, /* property disabled */ + { "GtkRecentChooserMenu", "select-multiple", (void*) MATCH_ANY_VALUE }, /* property disabled */ + { "GtkTextView", "overwrite", (void*) MATCH_ANY_VALUE }, /* needs text buffer */ + { "GtkToolbar", "icon-size", (void*) GTK_ICON_SIZE_INVALID }, + { NULL, NULL, NULL } + }; + /* properties suspected to be Gdk/Gtk+ bugs */ + static const IgnoreProperty bug_properties[] = { + { "GtkMessageDialog", "image", NULL, }, /* FIXME: should accept NULL images */ + { "GtkOptionMenu", "menu", NULL, }, /* FIXME: should accept NULL menus */ + { "GtkComboBox", "active", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers NULL model bug */ + { "GtkComboBoxEntry", "text-column", (void*) 0xffffffff }, /* FIXME: triggers signedness bug */ + { "GtkCTree", "indent", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers signedness bug */ + { "GtkCTree", "spacing", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers signedness bug */ + { "GtkCurve", "curve-type", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers OOM */ + { "GtkCurve", "min-x", (void*) 0x80000000 }, /* FIXME: triggers coordinate OOB */ + { "GtkCurve", "min-y", (void*) 0x80000000 }, /* FIXME: triggers coordinate OOB */ + { "GtkCurve", "max-x", (void*) 0x80000000 }, /* FIXME: triggers coordinate OOB */ + { "GtkCurve", "max-y", (void*) 0x80000000 }, /* FIXME: triggers coordinate OOB */ + { "GtkFileChooserButton", "local-only", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers NULL path assertion */ + { "GtkFileChooserDialog", "local-only", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers NULL path assertion */ + { "GtkFileChooserDialog", "action", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers closure->ref_count assertion */ + { "GtkFileChooserDialog", "visible", (void*) TRUE }, /* FIXME: triggers gtk_window_resize assertion */ + { "GtkFileChooserWidget", "local-only", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers NULL path assertion */ + { "GtkFontSelection", "font-name", (void*) MATCH_ANY_VALUE }, /* FIXME: requires non-NULL GdkScreen */ + { "GtkInvisible", "has-focus", (void*) TRUE }, /* FIXME: triggers invalid window cast */ + { "GtkInvisible", "is-focus", (void*) TRUE }, /* FIXME: triggers invalid window cast */ + { "GtkMenu", "tearoff-state", (void*) MATCH_ANY_VALUE }, /* FIXME: triggers NULL widget cast */ + { "GtkProgress", "activity-mode", (void*) TRUE }, /* FIXME: segfaults */ + { "GtkScaleButton", "adjustment", NULL, }, /* FIXME: should accept NULL adjustments */ + { "GtkStatusbar", "sensitive", (void*) FALSE }, /* FIXME: check if widget is realize */ + { "GtkTable", "n-rows", (void*) MATCH_ANY_VALUE }, /* FIXME: fix property minimum/maximum */ + { "GtkTable", "n-columns", (void*) MATCH_ANY_VALUE }, /* FIXME: fix property minimum/maximum */ + { "GtkText", "text-position", (void*) MATCH_ANY_VALUE }, /* FIXME: segfaults, fix property minimum/maximum */ + { NULL, NULL, NULL } + }; + if (buglist) + return bug_properties; + else + return ignore_properties; +} + +/* --- test functions --- */ +static void +pspec_select_value (GParamSpec *pspec, + GValue *value, + double dvalue) +{ + /* generate a value suitable for pspec */ + if (G_IS_PARAM_SPEC_CHAR (pspec)) + ASSIGN_VALUE (g_value_set_char, value, GParamSpecChar*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_UCHAR (pspec)) + ASSIGN_VALUE (g_value_set_uchar, value, GParamSpecUChar*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_INT (pspec)) + ASSIGN_VALUE (g_value_set_int, value, GParamSpecInt*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_UINT (pspec)) + ASSIGN_VALUE (g_value_set_uint, value, GParamSpecUInt*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_LONG (pspec)) + ASSIGN_VALUE (g_value_set_long, value, GParamSpecLong*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_ULONG (pspec)) + ASSIGN_VALUE (g_value_set_ulong, value, GParamSpecULong*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_INT64 (pspec)) + ASSIGN_VALUE (g_value_set_int64, value, GParamSpecInt64*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_UINT64 (pspec)) + ASSIGN_VALUE (g_value_set_uint64, value, GParamSpecUInt64*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_FLOAT (pspec)) + ASSIGN_VALUE (g_value_set_float, value, GParamSpecFloat*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) + ASSIGN_VALUE (g_value_set_double, value, GParamSpecDouble*, pspec, default_value, minimum, maximum, dvalue); + else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) + g_value_set_boolean (value, SELECT_VALUE (dvalue, ((GParamSpecBoolean*) pspec)->default_value, FALSE, TRUE)); + else if (G_IS_PARAM_SPEC_UNICHAR (pspec)) + g_value_set_uint (value, SELECT_VALUE (dvalue, ((GParamSpecUnichar*) pspec)->default_value, FALSE, TRUE)); + else if (G_IS_PARAM_SPEC_GTYPE (pspec)) + g_value_set_gtype (value, SELECT_VALUE ((int) dvalue, ((GParamSpecGType*) pspec)->is_a_type, 0, GTK_TYPE_WIDGET)); + else if (G_IS_PARAM_SPEC_STRING (pspec)) + { + GParamSpecString *sspec = (GParamSpecString*) pspec; + if (dvalue >= +2) + g_value_set_string (value, sspec->default_value); + if (dvalue > 0 && sspec->cset_first && sspec->cset_nth) + g_value_take_string (value, g_strdup_printf ("%c%c", sspec->cset_first[0], sspec->cset_nth[0])); + else /* if (sspec->ensure_non_null) */ + g_value_set_string (value, ""); + if (g_value_get_string (value) && strcmp (g_value_get_string (value), the_empty_string) == 0) + g_value_set_static_string (value, the_empty_string); // allow pointer comparisons of "" + } + else if (G_IS_PARAM_SPEC_ENUM (pspec)) + { + GParamSpecEnum *espec = (GParamSpecEnum*) pspec; + if (dvalue >= +2) + g_value_set_enum (value, espec->default_value); + if (dvalue >= 0 && dvalue <= 1) + g_value_set_enum (value, espec->enum_class->values[(int) ((espec->enum_class->n_values - 1) * dvalue)].value); + else if (dvalue <= -1) + g_value_set_enum (value, espec->enum_class->values[g_test_rand_int_range (0, espec->enum_class->n_values)].value); + } + else if (G_IS_PARAM_SPEC_FLAGS (pspec)) + { + GParamSpecFlags *fspec = (GParamSpecFlags*) pspec; + if (dvalue >= +2) + g_value_set_flags (value, fspec->default_value); + if (dvalue >= 0 && dvalue <= 1) + g_value_set_flags (value, fspec->flags_class->values[(int) ((fspec->flags_class->n_values - 1) * dvalue)].value); + else if (dvalue <= -1) + g_value_set_flags (value, fspec->flags_class->values[g_test_rand_int_range (0, fspec->flags_class->n_values)].value); + } + /* unimplemented: + * G_IS_PARAM_SPEC_PARAM + * G_IS_PARAM_SPEC_BOXED + * G_IS_PARAM_SPEC_POINTER + * G_IS_PARAM_SPEC_VALUE_ARRAY + * G_IS_PARAM_SPEC_OBJECT + */ +} + +static gpointer +value_as_pointer (GValue *value) +{ + if (g_value_fits_pointer (value)) + return g_value_peek_pointer (value); + if (G_VALUE_HOLDS_BOOLEAN (value)) + return (void*) g_value_get_boolean (value); + if (G_VALUE_HOLDS_CHAR (value)) + return (void*) (gssize) g_value_get_char (value); + if (G_VALUE_HOLDS_UCHAR (value)) + return (void*) (gsize) g_value_get_uchar (value); + if (G_VALUE_HOLDS_INT (value)) + return (void*) g_value_get_int (value); + if (G_VALUE_HOLDS_UINT (value)) + return (void*) g_value_get_uint (value); + if (G_VALUE_HOLDS_LONG (value)) + return (void*) g_value_get_long (value); + if (G_VALUE_HOLDS_ULONG (value)) + return (void*) g_value_get_ulong (value); + if (G_VALUE_HOLDS_FLOAT (value)) + return (void*) (gssize) g_value_get_float (value); + if (G_VALUE_HOLDS_DOUBLE (value)) + return (void*) (gssize) g_value_get_double (value); + if (G_VALUE_HOLDS_ENUM (value)) + return (void*) (gssize) g_value_get_enum (value); + if (G_VALUE_HOLDS_FLAGS (value)) + return (void*) (gsize) g_value_get_flags (value); + return (void*) 0x1373babe; +} + +static void +object_test_property (GObject *object, + GParamSpec *pspec, + double dvalue) +{ + /* test setting of a normal writable property */ + if (pspec->flags & G_PARAM_WRITABLE && + !(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))) + { + GValue value = { 0, }; + guint i; + const IgnoreProperty *ignore_properties; + /* select value to set */ + g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + pspec_select_value (pspec, &value, dvalue); + /* ignore untestable properties */ + ignore_properties = list_ignore_properties (FALSE); + for (i = 0; ignore_properties[i].name; i++) + if (g_type_is_a (G_OBJECT_TYPE (object), g_type_from_name (ignore_properties[i].type_name)) && + strcmp (pspec->name, ignore_properties[i].name) == 0 && + (MATCH_ANY_VALUE == ignore_properties[i].value || + value_as_pointer (&value) == ignore_properties[i].value)) + break; + /* ignore known property bugs if not testing thoroughly */ + if (ignore_properties[i].name == NULL && !g_test_thorough()) + { + ignore_properties = list_ignore_properties (TRUE); + for (i = 0; ignore_properties[i].name; i++) + if (g_type_is_a (G_OBJECT_TYPE (object), g_type_from_name (ignore_properties[i].type_name)) && + strcmp (pspec->name, ignore_properties[i].name) == 0 && + (MATCH_ANY_VALUE == ignore_properties[i].value || + value_as_pointer (&value) == ignore_properties[i].value)) + break; + } + /* assign unignored properties */ + if (ignore_properties[i].name == NULL) + { + if (g_test_verbose()) + g_print ("PropertyTest: %s::%s := (%s value (%s): %p)\n", + g_type_name (G_OBJECT_TYPE (object)), pspec->name, + SELECT_NAME (dvalue), g_type_name (G_VALUE_TYPE (&value)), + value_as_pointer (&value)); + g_object_set_property (object, pspec->name, &value); + } + /* cleanups */ + g_value_unset (&value); + } +} + +static void +widget_test_properties (GtkWidget *widget, + double dvalue) +{ + /* try setting all possible properties, according to dvalue */ + guint i, n_pspecs = 0; + GParamSpec **pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (widget), &n_pspecs); + for (i = 0; i < n_pspecs; i++) + { + GParamSpec *pspec = pspecs[i]; + if (pspec->flags & G_PARAM_WRITABLE && + !(pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))) + object_test_property (G_OBJECT (widget), pspecs[i], dvalue); + } + g_free (pspecs); +} + +static void +widget_fixups (GtkWidget *widget) +{ + /* post-constructor for widgets that need additional settings to work correctly */ + if (GTK_IS_COMBO_BOX_ENTRY (widget)) + { + GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); + g_object_set (widget, "model", store, "text-column", 0, NULL); + g_object_unref (store); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "test text"); + } + else if (GTK_IS_COMBO_BOX (widget)) + { + GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); + g_object_set (widget, "model", store, NULL); + g_object_unref (store); + gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "test text"); + } +} + +static void +widget_property_tests (gconstpointer test_data) +{ + GType wtype = (GType) test_data; + /* create widget */ + GtkWidget *widget = gtk_widget_new (wtype, NULL); + g_object_ref_sink (widget); + widget_fixups (widget); + /* test property values */ + widget_test_properties (widget, +2); /* test default_value */ + widget_test_properties (widget, 0); /* test minimum */ + widget_test_properties (widget, 0.5); /* test medium */ + widget_test_properties (widget, 1); /* test maximum */ + widget_test_properties (widget, -1); /* test random value */ + /* cleanup */ + gtk_widget_destroy (widget); + g_object_unref (widget); +} + +/* --- main test program --- */ +int +main (int argc, + char *argv[]) +{ + const GType *otypes; + guint i; + /* initialize test program */ + gtk_test_init (&argc, &argv); + gtk_test_register_all_types(); + /* install a property test for each widget type */ + otypes = gtk_test_list_all_types (NULL); + for (i = 0; otypes[i]; i++) + if (g_type_is_a (otypes[i], GTK_TYPE_WIDGET) && + G_TYPE_IS_OBJECT (otypes[i]) && + !G_TYPE_IS_ABSTRACT (otypes[i])) + { + gchar *testpath = g_strdup_printf ("/properties/%s", g_type_name (otypes[i])); + g_test_add_data_func (testpath, (void*) otypes[i], widget_property_tests); + g_free (testpath); + } + return g_test_run(); +}