glib/gobject/tests/param.c
Ryan Lortie c18462b580 GParamSpec: add g_param_spec_get_default_value()
The way of getting the default value out of a GParamSpec is to allocate
a GValue, initialise it, then call g_param_spec_set_default() to set the
default value into that GValue.

This is exactly how we handle setting the default value for all of the
construct properties that were not explicitly passed to g_object_new().

Instead of doing the alloc/init/store on all construct properties on
every call to g_object_new(), we can cache those GValues in the private
data of the GParamSpec itself and reuse them.

This patch does not actually make that change to g_object_new() yet, but
it adds the API to GParamSpec so that a future patch to GObject can make
the change.

https://bugzilla.gnome.org/show_bug.cgi?id=698056
2013-04-23 14:39:09 -04:00

820 lines
32 KiB
C

#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include <glib-object.h>
#include <stdlib.h>
static void
test_param_value (void)
{
GParamSpec *p, *p2;
GParamSpec *pp;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_PARAM);
g_assert (G_VALUE_HOLDS_PARAM (&value));
p = g_param_spec_int ("my-int", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
g_value_take_param (&value, p);
p2 = g_value_get_param (&value);
g_assert (p2 == p);
pp = g_param_spec_uint ("my-uint", "My UInt", "Blurb", 0, 10, 5, G_PARAM_READWRITE);
g_value_set_param (&value, pp);
p2 = g_value_dup_param (&value);
g_assert (p2 == pp); /* param specs use ref/unref for copy/free */
g_param_spec_unref (p2);
g_value_unset (&value);
g_param_spec_unref (pp);
}
static gint destroy_count;
static void
my_destroy (gpointer data)
{
destroy_count++;
}
static void
test_param_qdata (void)
{
GParamSpec *p;
gchar *bla;
GQuark q;
q = g_quark_from_string ("bla");
p = g_param_spec_int ("my-int", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
g_param_spec_set_qdata (p, q, "bla");
bla = g_param_spec_get_qdata (p, q);
g_assert_cmpstr (bla, ==, "bla");
g_assert_cmpint (destroy_count, ==, 0);
g_param_spec_set_qdata_full (p, q, "bla", my_destroy);
g_param_spec_set_qdata_full (p, q, "blabla", my_destroy);
g_assert_cmpint (destroy_count, ==, 1);
g_assert_cmpstr (g_param_spec_steal_qdata (p, q), ==, "blabla");
g_assert_cmpint (destroy_count, ==, 1);
g_assert (g_param_spec_get_qdata (p, q) == NULL);
g_param_spec_ref_sink (p);
g_param_spec_unref (p);
}
static void
test_param_validate (void)
{
GParamSpec *p;
GValue value = G_VALUE_INIT;
p = g_param_spec_int ("my-int", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, 100);
g_assert (!g_param_value_defaults (p, &value));
g_assert (g_param_value_validate (p, &value));
g_assert_cmpint (g_value_get_int (&value), ==, 20);
g_param_value_set_default (p, &value);
g_assert (g_param_value_defaults (p, &value));
g_assert_cmpint (g_value_get_int (&value), ==, 10);
g_param_spec_unref (p);
}
static void
test_param_strings (void)
{
GParamSpec *p;
/* test canonicalization */
p = g_param_spec_int ("my_int:bla", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
g_assert_cmpstr (g_param_spec_get_name (p), ==, "my-int-bla");
g_assert_cmpstr (g_param_spec_get_nick (p), ==, "My Int");
g_assert_cmpstr (g_param_spec_get_blurb (p), ==, "Blurb");
g_param_spec_unref (p);
/* test nick defaults to name */
p = g_param_spec_int ("my-int", NULL, NULL, 0, 20, 10, G_PARAM_READWRITE);
g_assert_cmpstr (g_param_spec_get_name (p), ==, "my-int");
g_assert_cmpstr (g_param_spec_get_nick (p), ==, "my-int");
g_assert (g_param_spec_get_blurb (p) == NULL);
g_param_spec_unref (p);
}
static void
test_param_convert (void)
{
GParamSpec *p;
GValue v1 = G_VALUE_INIT;
GValue v2 = G_VALUE_INIT;
p = g_param_spec_int ("my-int", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
g_value_init (&v1, G_TYPE_UINT);
g_value_set_uint (&v1, 43);
g_value_init (&v2, G_TYPE_INT);
g_value_set_int (&v2, -4);
g_assert (!g_param_value_convert (p, &v1, &v2, TRUE));
g_assert_cmpint (g_value_get_int (&v2), ==, -4);
g_assert (g_param_value_convert (p, &v1, &v2, FALSE));
g_assert_cmpint (g_value_get_int (&v2), ==, 20);
g_param_spec_unref (p);
}
static void
test_value_transform (void)
{
GValue src = G_VALUE_INIT;
GValue dest = G_VALUE_INIT;
#define CHECK_INT_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_INT, type)); \
g_value_init (&src, G_TYPE_INT); \
g_value_init (&dest, type); \
g_value_set_int (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpint (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
/* Keep a check for an integer in the range of 0-127 so we're
* still testing g_value_get_char(). See
* https://bugzilla.gnome.org/show_bug.cgi?id=659870
* for why it is broken.
*/
CHECK_INT_CONVERSION(G_TYPE_CHAR, char, 124)
CHECK_INT_CONVERSION(G_TYPE_CHAR, schar, -124)
CHECK_INT_CONVERSION(G_TYPE_CHAR, schar, 124)
CHECK_INT_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_INT_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_INT_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_INT_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_INT_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_INT_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_INT_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_INT_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_INT_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_INT_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_INT_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_INT_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_UINT_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_UINT, type)); \
g_value_init (&src, G_TYPE_UINT); \
g_value_init (&dest, type); \
g_value_set_uint (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpuint (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_UINT_CONVERSION(G_TYPE_CHAR, char, 124)
CHECK_UINT_CONVERSION(G_TYPE_CHAR, char, 124)
CHECK_UINT_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_UINT_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_UINT_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_UINT_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_UINT_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_UINT_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_UINT_CONVERSION(G_TYPE_LONG, long, 12345678)
CHECK_UINT_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_UINT_CONVERSION(G_TYPE_INT64, int64, 12345678)
CHECK_UINT_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_UINT_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_UINT_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_LONG_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_LONG, type)); \
g_value_init (&src, G_TYPE_LONG); \
g_value_init (&dest, type); \
g_value_set_long (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpint (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_LONG_CONVERSION(G_TYPE_CHAR, schar, -124)
CHECK_LONG_CONVERSION(G_TYPE_CHAR, schar, 124)
CHECK_LONG_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_LONG_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_LONG_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_LONG_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_LONG_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_LONG_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_LONG_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_LONG_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_LONG_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_LONG_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_LONG_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_LONG_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_ULONG_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_ULONG, type)); \
g_value_init (&src, G_TYPE_ULONG); \
g_value_init (&dest, type); \
g_value_set_ulong (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpuint (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_ULONG_CONVERSION(G_TYPE_CHAR, char, 124)
CHECK_ULONG_CONVERSION(G_TYPE_CHAR, char, 124)
CHECK_ULONG_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_ULONG_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_ULONG_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_ULONG_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_ULONG_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_ULONG_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_ULONG_CONVERSION(G_TYPE_LONG, long, 12345678)
CHECK_ULONG_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_ULONG_CONVERSION(G_TYPE_INT64, int64, 12345678)
CHECK_ULONG_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_ULONG_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_ULONG_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_INT64_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_INT64, type)); \
g_value_init (&src, G_TYPE_INT64); \
g_value_init (&dest, type); \
g_value_set_int64 (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpint (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_INT64_CONVERSION(G_TYPE_CHAR, schar, -124)
CHECK_INT64_CONVERSION(G_TYPE_CHAR, schar, 124)
CHECK_INT64_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_INT64_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_INT64_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_INT64_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_INT64_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_INT64_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_INT64_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_INT64_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_INT64_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_INT64_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_INT64_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_INT64_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_UINT64_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_UINT64, type)); \
g_value_init (&src, G_TYPE_UINT64); \
g_value_init (&dest, type); \
g_value_set_uint64 (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpuint (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_UINT64_CONVERSION(G_TYPE_CHAR, schar, -124)
CHECK_UINT64_CONVERSION(G_TYPE_CHAR, schar, 124)
CHECK_UINT64_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_UINT64_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_UINT64_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_UINT64_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_UINT64_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_UINT64_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_UINT64_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_UINT64_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_UINT64_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_UINT64_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_UINT64_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_UINT64_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_FLOAT_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_FLOAT, type)); \
g_value_init (&src, G_TYPE_FLOAT); \
g_value_init (&dest, type); \
g_value_set_float (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpfloat (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_FLOAT_CONVERSION(G_TYPE_CHAR, schar, -124)
CHECK_FLOAT_CONVERSION(G_TYPE_CHAR, schar, 124)
CHECK_FLOAT_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_FLOAT_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_FLOAT_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_FLOAT_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_FLOAT_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_FLOAT_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_FLOAT_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_FLOAT_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_FLOAT_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_FLOAT_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_FLOAT_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_FLOAT_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_DOUBLE_CONVERSION(type, getter, value) \
g_assert (g_value_type_transformable (G_TYPE_DOUBLE, type)); \
g_value_init (&src, G_TYPE_DOUBLE); \
g_value_init (&dest, type); \
g_value_set_double (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpfloat (g_value_get_##getter (&dest), ==, value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_DOUBLE_CONVERSION(G_TYPE_CHAR, schar, -124)
CHECK_DOUBLE_CONVERSION(G_TYPE_CHAR, schar, 124)
CHECK_DOUBLE_CONVERSION(G_TYPE_UCHAR, uchar, 0)
CHECK_DOUBLE_CONVERSION(G_TYPE_UCHAR, uchar, 255)
CHECK_DOUBLE_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_DOUBLE_CONVERSION(G_TYPE_INT, int, 12345)
CHECK_DOUBLE_CONVERSION(G_TYPE_UINT, uint, 0)
CHECK_DOUBLE_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_DOUBLE_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_DOUBLE_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_DOUBLE_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_DOUBLE_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_DOUBLE_CONVERSION(G_TYPE_FLOAT, float, 12345678)
CHECK_DOUBLE_CONVERSION(G_TYPE_DOUBLE, double, 12345678)
#define CHECK_BOOLEAN_CONVERSION(type, setter, value) \
g_assert (g_value_type_transformable (type, G_TYPE_BOOLEAN)); \
g_value_init (&src, type); \
g_value_init (&dest, G_TYPE_BOOLEAN); \
g_value_set_##setter (&src, value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpint (g_value_get_boolean (&dest), ==, TRUE); \
g_value_set_##setter (&src, 0); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpint (g_value_get_boolean (&dest), ==, FALSE); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_BOOLEAN_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_BOOLEAN_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_BOOLEAN_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_BOOLEAN_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_BOOLEAN_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_BOOLEAN_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
#define CHECK_STRING_CONVERSION(int_type, setter, int_value) \
g_assert (g_value_type_transformable (int_type, G_TYPE_STRING)); \
g_value_init (&src, int_type); \
g_value_init (&dest, G_TYPE_STRING); \
g_value_set_##setter (&src, int_value); \
g_assert (g_value_transform (&src, &dest)); \
g_assert_cmpstr (g_value_get_string (&dest), ==, #int_value); \
g_value_unset (&src); \
g_value_unset (&dest);
CHECK_STRING_CONVERSION(G_TYPE_INT, int, -12345)
CHECK_STRING_CONVERSION(G_TYPE_UINT, uint, 12345)
CHECK_STRING_CONVERSION(G_TYPE_LONG, long, -12345678)
CHECK_STRING_CONVERSION(G_TYPE_ULONG, ulong, 12345678)
CHECK_STRING_CONVERSION(G_TYPE_INT64, int64, -12345678)
CHECK_STRING_CONVERSION(G_TYPE_UINT64, uint64, 12345678)
CHECK_STRING_CONVERSION(G_TYPE_FLOAT, float, 0.500000)
CHECK_STRING_CONVERSION(G_TYPE_DOUBLE, double, -1.234567)
g_assert (!g_value_type_transformable (G_TYPE_STRING, G_TYPE_CHAR));
g_value_init (&src, G_TYPE_STRING);
g_value_init (&dest, G_TYPE_CHAR);
g_value_set_static_string (&src, "bla");
g_value_set_schar (&dest, 'c');
g_assert (!g_value_transform (&src, &dest));
g_assert_cmpint (g_value_get_schar (&dest), ==, 'c');
g_value_unset (&src);
g_value_unset (&dest);
}
/* We create some dummy objects with a simple relationship:
*
* GObject
* / \
* TestObjectA TestObjectC
* |
* TestObjectB
*
* ie: TestObjectB is a subclass of TestObjectA and TestObjectC is
* related to neither.
*/
static GType test_object_a_get_type (void);
typedef GObject TestObjectA; typedef GObjectClass TestObjectAClass;
G_DEFINE_TYPE (TestObjectA, test_object_a, G_TYPE_OBJECT);
static void test_object_a_class_init (TestObjectAClass *class) { }
static void test_object_a_init (TestObjectA *a) { }
static GType test_object_b_get_type (void);
typedef GObject TestObjectB; typedef GObjectClass TestObjectBClass;
G_DEFINE_TYPE (TestObjectB, test_object_b, test_object_a_get_type ());
static void test_object_b_class_init (TestObjectBClass *class) { }
static void test_object_b_init (TestObjectB *b) { }
static GType test_object_c_get_type (void);
typedef GObject TestObjectC; typedef GObjectClass TestObjectCClass;
G_DEFINE_TYPE (TestObjectC, test_object_c, G_TYPE_OBJECT);
static void test_object_c_class_init (TestObjectCClass *class) { }
static void test_object_c_init (TestObjectC *c) { }
/* We create an interface and programmatically populate it with
* properties of each of the above type, with various flag combinations.
*
* Properties are named like "type-perm" where type is 'a', 'b' or 'c'
* and perm is a series of characters, indicating the permissions:
*
* - 'r': readable
* - 'w': writable
* - 'c': construct
* - 'C': construct-only
*
* It doesn't make sense to have a property that is neither readable nor
* writable. It is also not valid to have construct or construct-only
* on read-only params. Finally, it is invalid to have both construct
* and construct-only specified, so we do not consider those cases.
* That gives us 7 possible permissions:
*
* 'r', 'w', 'rw', 'wc', 'rwc', 'wC', 'rwC'
*
* And 9 impossible ones:
*
* '', 'c', 'rc', 'C', 'rC', 'cC', 'rcC', 'wcC', rwcC'
*
* For a total of 16 combinations.
*
* That gives a total of 48 (16 * 3) possible flag/type combinations, of
* which 27 (9 * 3) are impossible to install.
*
* That gives 21 (7 * 3) properties that will be installed.
*/
typedef GTypeInterface TestInterfaceInterface;
static GType test_interface_get_type (void);
//typedef struct _TestInterface TestInterface;
G_DEFINE_INTERFACE (TestInterface, test_interface, G_TYPE_OBJECT)
static void
test_interface_default_init (TestInterfaceInterface *iface)
{
const gchar *names[] = { "a", "b", "c" };
const gchar *perms[] = { NULL, "r", "w", "rw",
NULL, NULL, "wc", "rwc",
NULL, NULL, "wC", "rwC",
NULL, NULL, NULL, NULL };
const GType types[] = { test_object_a_get_type (), test_object_b_get_type (), test_object_c_get_type () };
guint i, j;
for (i = 0; i < G_N_ELEMENTS (types); i++)
for (j = 0; j < G_N_ELEMENTS (perms); j++)
{
gchar prop_name[10];
GParamSpec *pspec;
if (perms[j] == NULL)
{
if (!g_test_undefined ())
continue;
/* we think that this is impossible. make sure. */
pspec = g_param_spec_object ("xyz", "xyz", "xyz", types[i], j);
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*assertion*pspec->flags*failed*");
g_object_interface_install_property (iface, pspec);
g_test_assert_expected_messages ();
g_param_spec_unref (pspec);
continue;
}
/* install the property */
g_snprintf (prop_name, sizeof prop_name, "%s-%s", names[i], perms[j]);
pspec = g_param_spec_object (prop_name, prop_name, prop_name, types[i], j);
g_object_interface_install_property (iface, pspec);
}
}
/* We now have 21 properties. Each property may be correctly
* implemented with the following types:
*
* Properties Valid Types Reason
*
* a-r a, b Read only can provide subclasses
* a-w, wc, wC a, GObject Write only can accept superclasses
* a-rw, rwc, rwC a Read-write must be exactly equal
*
* b-r b (as above)
* b-w, wc, wC b, a, GObject
* b-rw, rwc, rwC b
*
* c-r c (as above)
* c-wo, wc, wC c, GObject
* c-rw, rwc, rwC c
*
* We can express this in a 48-by-4 table where each row represents an
* installed property and each column represents a type. The value in
* the table represents if it is valid to subclass the row's property
* with the type of the column:
*
* - 0: invalid because the interface property doesn't exist (invalid flags)
* - 'v': valid
* - '=': invalid because of the type not being exactly equal
* - '<': invalid because of the type not being a subclass
* - '>': invalid because of the type not being a superclass
*
* We organise the table by interface property type ('a', 'b', 'c') then
* by interface property flags.
*/
static gint valid_impl_types[48][4] = {
/* a b c GObject */
/* 'a-' */ { },
/* 'a-r' */ { 'v', 'v', '<', '<' },
/* 'a-w' */ { 'v', '>', '>', 'v' },
/* 'a-rw' */ { 'v', '=', '=', '=' },
/* 'a-c */ { },
/* 'a-rc' */ { },
/* 'a-wc' */ { 'v', '>', '>', 'v' },
/* 'a-rwc' */ { 'v', '=', '=', '=' },
/* 'a-C */ { },
/* 'a-rC' */ { },
/* 'a-wC' */ { 'v', '>', '>', 'v' },
/* 'a-rwC' */ { 'v', '=', '=', '=' },
/* 'a-cC */ { },
/* 'a-rcC' */ { },
/* 'a-wcC' */ { },
/* 'a-rwcC' */ { },
/* 'b-' */ { },
/* 'b-r' */ { '<', 'v', '<', '<' },
/* 'b-w' */ { 'v', 'v', '>', 'v' },
/* 'b-rw' */ { '=', 'v', '=', '=' },
/* 'b-c */ { },
/* 'b-rc' */ { },
/* 'b-wc' */ { 'v', 'v', '>', 'v' },
/* 'b-rwc' */ { '=', 'v', '=', '=' },
/* 'b-C */ { },
/* 'b-rC' */ { },
/* 'b-wC' */ { 'v', 'v', '>', 'v' },
/* 'b-rwC' */ { '=', 'v', '=', '=' },
/* 'b-cC */ { },
/* 'b-rcC' */ { },
/* 'b-wcC' */ { },
/* 'b-rwcC' */ { },
/* 'c-' */ { },
/* 'c-r' */ { '<', '<', 'v', '<' },
/* 'c-w' */ { '>', '>', 'v', 'v' },
/* 'c-rw' */ { '=', '=', 'v', '=' },
/* 'c-c */ { },
/* 'c-rc' */ { },
/* 'c-wc' */ { '>', '>', 'v', 'v' },
/* 'c-rwc' */ { '=', '=', 'v', '=' },
/* 'c-C */ { },
/* 'c-rC' */ { },
/* 'c-wC' */ { '>', '>', 'v', 'v' },
/* 'c-rwC' */ { '=', '=', 'v', '=' },
/* 'c-cC */ { },
/* 'c-rcC' */ { },
/* 'c-wcC' */ { },
/* 'c-rwcC' */ { }
};
/* We also try to change the flags. We must ensure that all
* implementations provide all functionality promised by the interface.
* We must therefore never remove readability or writability (but we can
* add them). Construct-only is a restrictions that applies to
* writability, so we can never add it unless writability was never
* present in the first place, in which case "writable at construct
* only" is still better than "not writable".
*
* The 'construct' flag is of interest only to the implementation. It
* may be changed at any time.
*
* Properties Valid Access Reason
*
* *-r r, rw, rwc, rwC Must keep readable, but can restrict newly-added writable
* *-w w, rw, rwc Must keep writable unrestricted
* *-rw rw, rwc Must not add any restrictions
* *-rwc rw, rwc Must not add any restrictions
* *-rwC rw, rwc, rwC Can remove 'construct-only' restriction
* *-wc rwc, rw, w, wc Can add readability
* *-wC rwC, rw, w, wC Can add readability or remove 'construct only' restriction
* rwc, wc
*
* We can represent this with a 16-by-16 table. The rows represent the
* flags of the property on the interface. The columns is the flags to
* try to use when overriding the property. The cell contents are:
*
* - 0: invalid because the interface property doesn't exist (invalid flags)
* - 'v': valid
* - 'i': invalid because the implementation flags are invalid
* - 'f': invalid because of the removal of functionality
* - 'r': invalid because of the addition of restrictions (ie: construct-only)
*
* We also ensure that removal of functionality is reported before
* addition of restrictions, since this is a more basic problem.
*/
static gint valid_impl_flags[16][16] = {
/* '' r w rw c rc wc rwc C rC wC rwC cC rcC wcC rwcC */
/* '*-' */ { },
/* '*-r' */ { 'i', 'v', 'f', 'v', 'i', 'i', 'f', 'v', 'i', 'i', 'f', 'v', 'i', 'i', 'i', 'i' },
/* '*-w' */ { 'i', 'f', 'v', 'v', 'i', 'i', 'v', 'v', 'i', 'i', 'r', 'r', 'i', 'i', 'i', 'i' },
/* '*-rw' */ { 'i', 'f', 'f', 'v', 'i', 'i', 'f', 'v', 'i', 'i', 'f', 'r', 'i', 'i', 'i', 'i' },
/* '*-c */ { },
/* '*-rc' */ { },
/* '*-wc' */ { 'i', 'f', 'v', 'v', 'i', 'i', 'v', 'v', 'i', 'i', 'r', 'r', 'i', 'i', 'i', 'i' },
/* '*-rwc' */ { 'i', 'f', 'f', 'v', 'i', 'i', 'f', 'v', 'i', 'i', 'f', 'r', 'i', 'i', 'i', 'i' },
/* '*-C */ { },
/* '*-rC' */ { },
/* '*-wC' */ { 'i', 'f', 'v', 'v', 'i', 'i', 'v', 'v', 'i', 'i', 'v', 'v', 'i', 'i', 'i', 'i' },
/* '*-rwC' */ { 'i', 'f', 'f', 'v', 'i', 'i', 'f', 'v', 'i', 'i', 'f', 'v', 'i', 'i', 'i', 'i' },
};
static guint change_this_flag;
static guint change_this_type;
static guint use_this_flag;
static guint use_this_type;
typedef GObjectClass TestImplementationClass;
typedef GObject TestImplementation;
static void test_implementation_init (TestImplementation *impl) { }
static void test_implementation_iface_init (TestInterfaceInterface *iface) { }
static GType test_implementation_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestImplementation, test_implementation, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_implementation_iface_init))
static void test_implementation_class_init (TestImplementationClass *class)
{
const gchar *names[] = { "a", "b", "c" };
const gchar *perms[] = { NULL, "r", "w", "rw",
NULL, NULL, "wc", "rwc",
NULL, NULL, "wC", "rwC",
NULL, NULL, NULL, NULL };
const GType types[] = { test_object_a_get_type (), test_object_b_get_type (),
test_object_c_get_type (), G_TYPE_OBJECT };
gchar prop_name[10];
GParamSpec *pspec;
guint i, j;
class->get_property = GINT_TO_POINTER (1);
class->set_property = GINT_TO_POINTER (1);
/* Install all of the non-modified properties or else GObject will
* complain about non-implemented properties.
*/
for (i = 0; i < 3; i++)
for (j = 0; j < G_N_ELEMENTS (perms); j++)
{
if (i == change_this_type && j == change_this_flag)
continue;
if (perms[j] != NULL)
{
/* override the property without making changes */
g_snprintf (prop_name, sizeof prop_name, "%s-%s", names[i], perms[j]);
g_object_class_override_property (class, 1, prop_name);
}
}
/* Now try installing our modified property */
if (perms[change_this_flag] == NULL)
g_error ("Interface property does not exist");
g_snprintf (prop_name, sizeof prop_name, "%s-%s", names[change_this_type], perms[change_this_flag]);
pspec = g_param_spec_object (prop_name, prop_name, prop_name, types[use_this_type], use_this_flag);
g_object_class_install_property (class, 1, pspec);
}
static void
test_param_implement (void)
{
/* GObject oddity: GObjectClass must be initialised before we can
* initialise a GTypeInterface.
*/
g_type_class_ref (G_TYPE_OBJECT);
/* Bring up the interface first. */
g_type_default_interface_ref (test_interface_get_type ());
for (change_this_flag = 0; change_this_flag < 16; change_this_flag++)
for (change_this_type = 0; change_this_type < 3; change_this_type++)
for (use_this_flag = 0; use_this_flag < 16; use_this_flag++)
for (use_this_type = 0; use_this_type < 4; use_this_type++)
{
if (!g_test_undefined ())
{
/* only test the valid (defined) cases, e.g. under valgrind */
if (valid_impl_flags[change_this_flag][use_this_flag] != 'v')
continue;
if (valid_impl_types[change_this_type * 16 + change_this_flag][use_this_type] != 'v')
continue;
}
if (g_test_trap_fork (G_TIME_SPAN_SECOND, G_TEST_TRAP_SILENCE_STDERR))
{
g_type_class_ref (test_implementation_get_type ());
exit (0);
}
/* We want to ensure that any flags mismatch problems are reported first. */
switch (valid_impl_flags[change_this_flag][use_this_flag])
{
case 0:
/* make sure the other table agrees */
g_assert (valid_impl_types[change_this_type * 16 + change_this_flag][use_this_type] == 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*Interface property does not exist*");
continue;
case 'i':
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*g_object_class_install_property*");
continue;
case 'f':
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*remove functionality*");
continue;
case 'r':
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*introduce additional restrictions*");
continue;
case 'v':
break;
}
/* Next, we check if there should have been a type error. */
switch (valid_impl_types[change_this_type * 16 + change_this_flag][use_this_type])
{
case 0:
/* this should have been caught above */
g_assert_not_reached ();
case '=':
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*exactly equal*");
continue;
case '<':
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*equal to or more restrictive*");
continue;
case '>':
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*equal to or less restrictive*");
continue;
case 'v':
break;
}
g_test_trap_assert_passed ();
}
}
static void
test_param_default (void)
{
GParamSpec *param;
const GValue *def;
param = g_param_spec_int ("my-int", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
def = g_param_spec_get_default_value (param);
g_assert (G_VALUE_HOLDS (def, G_TYPE_INT));
g_assert_cmpint (g_value_get_int (def), ==, 10);
g_param_spec_unref (param);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/param/value", test_param_value);
g_test_add_func ("/param/strings", test_param_strings);
g_test_add_func ("/param/qdata", test_param_qdata);
g_test_add_func ("/param/validate", test_param_validate);
g_test_add_func ("/param/convert", test_param_convert);
g_test_add_func ("/param/implement", test_param_implement);
g_test_add_func ("/value/transform", test_value_transform);
g_test_add_func ("/param/default", test_param_default);
return g_test_run ();
}