The code used to check for uninstalled gdk-pixbuf. Since gdk-pixbuf is an external library these days, those checks aren't necessary anymore.
		
			
				
	
	
		
			330 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
#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)
 | 
						|
 | 
						|
/* --- 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*) "", },                  /* "" 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 */
 | 
						|
    { "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 */
 | 
						|
    { "GtkMenu",                "accel-path",           (void*) MATCH_ANY_VALUE },      /* has odd restrictions in the setter */
 | 
						|
    { "GtkMenuItem",            "accel-path",           (void*) MATCH_ANY_VALUE },      /* has odd restrictions in the setter */
 | 
						|
    { "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[] = {
 | 
						|
    { "GtkComboBox",            "active",               (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL model bug */
 | 
						|
    { "GtkCTree",               "spacing",              (void*) MATCH_ANY_VALUE },      /* FIXME: triggers signedness bug */
 | 
						|
    { "GtkFileChooserButton",   "local-only",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL path assertion */
 | 
						|
    { "GtkFileChooserDialog",   "local-only",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL path assertion */
 | 
						|
    { "GtkFileChooserWidget",   "local-only",           (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL path assertion */
 | 
						|
    { "GtkMenu",                "tearoff-state",        (void*) MATCH_ANY_VALUE },      /* FIXME: triggers NULL widget cast */
 | 
						|
    { "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, "");
 | 
						|
    }
 | 
						|
  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 GINT_TO_POINTER(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 GINT_TO_POINTER(g_value_get_int (value));
 | 
						|
  if (G_VALUE_HOLDS_UINT (value))
 | 
						|
    return GUINT_TO_POINTER(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 = G_VALUE_INIT;
 | 
						|
      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_strcmp0 ("", ignore_properties[i].name) ||
 | 
						|
            (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 ||
 | 
						|
              (G_VALUE_HOLDS_STRING (&value) &&
 | 
						|
               strcmp (g_value_get_string (&value), ignore_properties[i].value) == 0))))
 | 
						|
          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 ||
 | 
						|
                 (G_VALUE_HOLDS_STRING (&value) &&
 | 
						|
                  strcmp (g_value_get_string (&value), ignore_properties[i].value) == 0)))
 | 
						|
              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 (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 ();
 | 
						|
}
 |