#define GLIB_DISABLE_DEPRECATION_WARNINGS #include #include 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-' */ { 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 (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[]) { 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_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); return g_test_run (); }