glib/gobject/tests/param.c
Jehan 13d1697b67 gobject: Add g_{param_spec,signal}_is_valid_name() functions
Making this validation code public allows projects to validate a
GParamSpec name before creating it. While hard-coded GParamSpec don't
need this, we can't afford crashing the main program for dynamically
generated GParamSpec from user-created data.

In such case, we will need to validate the param names and return errors
instead of trying to create a GParamSpec with invalid names.

Includes modifications from Philip Withnall and Emmanuele Bassi to
rearrange the new function addition and split it into one function for
GParamSpecs and one for GSignals.
2020-03-04 14:46:28 +00:00

914 lines
36 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_true (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_true (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_true (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_null (g_param_spec_get_qdata (p, q));
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_false (g_param_value_defaults (p, &value));
g_assert_true (g_param_value_validate (p, &value));
g_assert_cmpint (g_value_get_int (&value), ==, 20);
g_param_value_set_default (p, &value);
g_assert_true (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", "My Int", "Blurb", 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_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_null (g_param_spec_get_blurb (p));
g_param_spec_unref (p);
}
static void
test_param_invalid_name (gconstpointer test_data)
{
const gchar *invalid_name = test_data;
g_test_summary ("Test that properties cannot be created with invalid names");
if (g_test_subprocess ())
{
GParamSpec *p;
p = g_param_spec_int (invalid_name, "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
g_param_spec_unref (p);
return;
}
g_test_trap_subprocess (NULL, 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*CRITICAL*g_param_spec_is_valid_name (name)*");
}
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_false (g_param_value_convert (p, &v1, &v2, TRUE));
g_assert_cmpint (g_value_get_int (&v2), ==, -4);
g_assert_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (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_true (g_value_transform (&src, &dest)); \
g_assert_cmpint (g_value_get_boolean (&dest), ==, TRUE); \
g_value_set_##setter (&src, 0); \
g_assert_true (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_true (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_true (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_false (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_false (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-' */ { 0, },
/* 'a-r' */ { 'v', 'v', '<', '<' },
/* 'a-w' */ { 'v', '>', '>', 'v' },
/* 'a-rw' */ { 'v', '=', '=', '=' },
/* 'a-c */ { 0, },
/* 'a-rc' */ { 0, },
/* 'a-wc' */ { 'v', '>', '>', 'v' },
/* 'a-rwc' */ { 'v', '=', '=', '=' },
/* 'a-C */ { 0, },
/* 'a-rC' */ { 0, },
/* 'a-wC' */ { 'v', '>', '>', 'v' },
/* 'a-rwC' */ { 'v', '=', '=', '=' },
/* 'a-cC */ { 0, },
/* 'a-rcC' */ { 0, },
/* 'a-wcC' */ { 0, },
/* 'a-rwcC' */ { 0, },
/* 'b-' */ { 0, },
/* 'b-r' */ { '<', 'v', '<', '<' },
/* 'b-w' */ { 'v', 'v', '>', 'v' },
/* 'b-rw' */ { '=', 'v', '=', '=' },
/* 'b-c */ { 0, },
/* 'b-rc' */ { 0, },
/* 'b-wc' */ { 'v', 'v', '>', 'v' },
/* 'b-rwc' */ { '=', 'v', '=', '=' },
/* 'b-C */ { 0, },
/* 'b-rC' */ { 0, },
/* 'b-wC' */ { 'v', 'v', '>', 'v' },
/* 'b-rwC' */ { '=', 'v', '=', '=' },
/* 'b-cC */ { 0, },
/* 'b-rcC' */ { 0, },
/* 'b-wcC' */ { 0, },
/* 'b-rwcC' */ { 0, },
/* 'c-' */ { 0, },
/* 'c-r' */ { '<', '<', 'v', '<' },
/* 'c-w' */ { '>', '>', 'v', 'v' },
/* 'c-rw' */ { '=', '=', 'v', '=' },
/* 'c-c */ { 0, },
/* 'c-rc' */ { 0, },
/* 'c-wc' */ { '>', '>', 'v', 'v' },
/* 'c-rwc' */ { '=', '=', 'v', '=' },
/* 'c-C */ { 0, },
/* 'c-rC' */ { 0, },
/* 'c-wC' */ { '>', '>', 'v', 'v' },
/* 'c-rwC' */ { '=', '=', 'v', '=' },
/* 'c-cC */ { 0, },
/* 'c-rcC' */ { 0, },
/* 'c-wcC' */ { 0, },
/* 'c-rwcC' */ { 0, }
};
/* 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 */
/* '*-' */ { 0, },
/* '*-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 */ { 0, },
/* '*-rc' */ { 0, },
/* '*-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 */ { 0, },
/* '*-rC' */ { 0, },
/* '*-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);
}
typedef struct {
gint change_this_flag;
gint change_this_type;
gint use_this_flag;
gint use_this_type;
} TestParamImplementData;
static void
test_param_implement_child (gconstpointer user_data)
{
TestParamImplementData *data = (gpointer) user_data;
/* 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 ());
/* Copy the flags into the global vars so
* test_implementation_class_init() will see them.
*/
change_this_flag = data->change_this_flag;
change_this_type = data->change_this_type;
use_this_flag = data->use_this_flag;
use_this_type = data->use_this_type;
g_type_class_ref (test_implementation_get_type ());
}
static void
test_param_implement (void)
{
gchar *test_path;
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;
}
test_path = g_strdup_printf ("/param/implement/subprocess/%d-%d-%d-%d",
change_this_flag, change_this_type,
use_this_flag, use_this_type);
g_test_trap_subprocess (test_path, G_TIME_SPAN_SECOND, 0);
g_free (test_path);
/* 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_cmpint (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_true (G_VALUE_HOLDS (def, G_TYPE_INT));
g_assert_cmpint (g_value_get_int (def), ==, 10);
g_param_spec_unref (param);
}
static void
test_param_is_valid_name (void)
{
const gchar *valid_names[] =
{
"property",
"i",
"multiple-segments",
"segment0-SEGMENT1",
"using_underscores",
};
const gchar *invalid_names[] =
{
"",
"7zip",
"my_int:hello",
};
gsize i;
for (i = 0; i < G_N_ELEMENTS (valid_names); i++)
g_assert_true (g_param_spec_is_valid_name (valid_names[i]));
for (i = 0; i < G_N_ELEMENTS (invalid_names); i++)
g_assert_false (g_param_spec_is_valid_name (invalid_names[i]));
}
int
main (int argc, char *argv[])
{
TestParamImplementData data, *test_data;
gchar *test_path;
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_data_func ("/param/invalid-name/colon", "my_int:hello", test_param_invalid_name);
g_test_add_data_func ("/param/invalid-name/first-char", "7zip", test_param_invalid_name);
g_test_add_data_func ("/param/invalid-name/empty", "", test_param_invalid_name);
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);
if (g_test_slow ())
g_test_add_func ("/param/implement", test_param_implement);
for (data.change_this_flag = 0; data.change_this_flag < 16; data.change_this_flag++)
for (data.change_this_type = 0; data.change_this_type < 3; data.change_this_type++)
for (data.use_this_flag = 0; data.use_this_flag < 16; data.use_this_flag++)
for (data.use_this_type = 0; data.use_this_type < 4; data.use_this_type++)
{
test_path = g_strdup_printf ("/param/implement/subprocess/%d-%d-%d-%d",
data.change_this_flag, data.change_this_type,
data.use_this_flag, data.use_this_type);
test_data = g_memdup (&data, sizeof (TestParamImplementData));
g_test_add_data_func_full (test_path, test_data, test_param_implement_child, g_free);
g_free (test_path);
}
g_test_add_func ("/value/transform", test_value_transform);
g_test_add_func ("/param/default", test_param_default);
g_test_add_func ("/param/is-valid-name", test_param_is_valid_name);
return g_test_run ();
}