]> Pileus Git - ~andy/gtk/commitdiff
run tests in current dir after setting up the logging directory, so their
author18:59:59 Tim Janik <timj@imendio.com>
Wed, 5 Dec 2007 16:58:44 +0000 (16:58 +0000)
committerTim Janik <timj@src.gnome.org>
Wed, 5 Dec 2007 16:58:44 +0000 (16:58 +0000)
2007-12-05 18:59:59  Tim Janik  <timj@imendio.com>

        * 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

ChangeLog
Makefile.decl
gtk/Makefile.am
gtk/gtk.symbols
gtk/gtktestutils.c
gtk/gtktestutils.h
tests/Makefile.am
tests/objecttests.c [new file with mode: 0644]

index 8f59f21c4064c2fbb5adc4dcffeb9f6eff9c3059..5d2b49b30cd96374e0b60524aab408114f4375e1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2007-12-05 18:59:59  Tim Janik  <timj@imendio.com>
+
+       * 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  <richard@imendio.com>
 
        * gdk/quartz/gdkwindow-quartz.c: (_gdk_windowing_window_init):
index 2963f2bd4fd4121310319ead78a9a6dc0fdab3c7..4caa19b7725eabf5dcf5478a2bd2b9fb46118b35 100644 (file)
@@ -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 version="1.0"?>' > $@.xml ; \
            echo '<report-collection>'  >> $@.xml ; \
index 7e1395365d3f27686ac1a6eab49cffc72d526a0c..359d1c9cda5889741d25fb76ced3539eb9bb0ec0 100644 (file)
@@ -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
 
index c657cc82086e5c10c32224db9af92cfd55f499b6..f243301646729aa21e5ce25ea66c733d6c3e2691 100644 (file)
@@ -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
index 001a6a8e78d625b52c81a68ebae4b45b040f4cfb..cafc65723e92359ab78b8b41d68c4769726a9a62 100644 (file)
@@ -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__
index 412ca8130a11a6415764fadef13c2c78e1a4e7d7..bcb2c12fa38f5275348f63661871e6f40935b5c8 100644 (file)
@@ -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);
index d704321417010b69ee71b195d75ee5d8fa73d946..7f41e2eef83451ab4c803c879907e2b47049d7e2 100644 (file)
@@ -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 (file)
index 0000000..62d5e14
--- /dev/null
@@ -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 <gtk/gtk.h>
+#include <string.h>
+
+/* --- 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();
+}