gtk2/testsuite/gtk/object.c
Christoph Reiter fb083b6de2 tests: don't overwrite GSETTINGS_SCHEMA_DIR when running under meson
Under autotools the compiled schemas are in the build directory and with
meson they are in the root build dir. Avoid changing the autotools build for
now and add a special GTK_TEST_MESON env var which we can use to differentiate
the two.

See 1253e7bfcb for a similar fix on master.
2019-04-11 16:45:36 +02:00

357 lines
17 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 */
{ "GtkInvisible", "screen", (void*) MATCH_ANY_VALUE }, /* cannot create GdkScreen */
{ "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 },
{ "GtkTreeView", "expander-column", (void*) MATCH_ANY_VALUE }, /* assertion list != NULL */
{ "GtkWindow", "screen", (void*) MATCH_ANY_VALUE }, /* cannot create GdkScreen */
{ 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_schar, 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);
}
else if (G_IS_PARAM_SPEC_OBJECT (pspec))
{
gpointer object = NULL;
if (!G_TYPE_IS_ABSTRACT (pspec->value_type) &&
!G_TYPE_IS_INTERFACE (pspec->value_type))
{
if (g_type_is_a (pspec->value_type, GDK_TYPE_PIXBUF))
object = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 32, 32);
else if (g_type_is_a (pspec->value_type, GDK_TYPE_PIXBUF_ANIMATION))
object = gdk_pixbuf_simple_anim_new (32, 32, 15);
else
object = g_object_new (pspec->value_type, NULL);
g_object_ref_sink (object);
g_value_take_object (value, object);
}
}
/* unimplemented:
* G_IS_PARAM_SPEC_PARAM
* G_IS_PARAM_SPEC_BOXED
* G_IS_PARAM_SPEC_POINTER
* G_IS_PARAM_SPEC_VALUE_ARRAY
*/
}
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_schar (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 GSIZE_TO_POINTER ((gssize) g_value_get_long (value));
if (G_VALUE_HOLDS_ULONG (value))
return GSIZE_TO_POINTER (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) &&
g_strcmp0 (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) &&
g_strcmp0 (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_TEXT (widget))
{
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (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;
gchar *schema_dir;
g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
/* initialize test program */
gtk_test_init (&argc, &argv);
gtk_test_register_all_types ();
/* g_test_build_filename must be called after gtk_test_init */
schema_dir = g_test_build_filename (G_TEST_BUILT, "", NULL);
if (g_getenv ("GTK_TEST_MESON") == NULL)
g_setenv ("GSETTINGS_SCHEMA_DIR", schema_dir, TRUE);
/* 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);
}
g_free (schema_dir);
return g_test_run ();
}