Import Upstream version 2.72.4

This commit is contained in:
evinadmin 2023-07-04 11:23:22 +02:00
commit 4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions

18
gobject/tests/.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
binding
boxed
closure
dynamictests
enums
ifaceproperties
object
param
properties
qdata
reference
signal-handler
signals
threadtests
type
value
private
marshalers.[ch]

237
gobject/tests/autoptr.c Normal file
View file

@ -0,0 +1,237 @@
/* GLib testing framework examples and tests
* Copyright (C) 2018 Canonical Ltd
* Authors: Marco Trevisan <marco@ubuntu.com>
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib-object.h>
#include <string.h>
G_DECLARE_DERIVABLE_TYPE (TestAutoCleanupBase, test_base_auto_cleanup, TEST, BASE_AUTO_CLEANUP, GObject)
struct _TestAutoCleanupBaseClass {
GObjectClass parent_class;
};
G_DEFINE_TYPE (TestAutoCleanupBase, test_base_auto_cleanup, G_TYPE_OBJECT)
static void
test_base_auto_cleanup_class_init (TestAutoCleanupBaseClass *class)
{
}
static void
test_base_auto_cleanup_init (TestAutoCleanupBase *tac)
{
}
G_DECLARE_FINAL_TYPE (TestAutoCleanup, test_auto_cleanup, TEST, AUTO_CLEANUP, TestAutoCleanupBase)
struct _TestAutoCleanup
{
TestAutoCleanupBase parent_instance;
};
G_DEFINE_TYPE (TestAutoCleanup, test_auto_cleanup, G_TYPE_OBJECT)
static void
test_auto_cleanup_class_init (TestAutoCleanupClass *class)
{
}
static void
test_auto_cleanup_init (TestAutoCleanup *tac)
{
}
static TestAutoCleanup *
test_auto_cleanup_new (void)
{
return g_object_new (test_auto_cleanup_get_type (), NULL);
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autocleanup functions, defined using the ones of the base type (defined with
* G_DECLARE_DERIVABLE_TYPE) and so that it can be used with g_autoptr */
static void
test_autoptr (void)
{
TestAutoCleanup *tac_ptr = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac_ptr), (gpointer *) &tac_ptr);
{
g_autoptr (TestAutoCleanup) tac = tac_ptr;
g_assert_nonnull (tac);
}
#ifdef __GNUC__
g_assert_null (tac_ptr);
#endif
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autocleanup functions, defined using the ones of the base type (defined with
* G_DECLARE_DERIVABLE_TYPE) and that stealing an autopointer works properly */
static void
test_autoptr_steal (void)
{
g_autoptr (TestAutoCleanup) tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac_ptr = tac1;
g_object_add_weak_pointer (G_OBJECT (tac_ptr), (gpointer *) &tac_ptr);
{
g_autoptr (TestAutoCleanup) tac2 = g_steal_pointer (&tac1);
g_assert_nonnull (tac_ptr);
g_assert_null (tac1);
g_assert_true (tac2 == tac_ptr);
}
#ifdef __GNUC__
g_assert_null (tac_ptr);
#endif
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autolist cleanup functions defined using the ones of the parent type
* and so that can be used with g_autolist, and that freeing the list correctly
* unrefs the object too */
static void
test_autolist (void)
{
TestAutoCleanup *tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac2 = test_auto_cleanup_new ();
g_autoptr (TestAutoCleanup) tac3 = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac1), (gpointer *) &tac1);
g_object_add_weak_pointer (G_OBJECT (tac2), (gpointer *) &tac2);
g_object_add_weak_pointer (G_OBJECT (tac3), (gpointer *) &tac3);
{
g_autolist (TestAutoCleanup) l = NULL;
l = g_list_prepend (l, tac1);
l = g_list_prepend (l, tac2);
/* Squash warnings about dead stores */
(void) l;
}
/* Only assert if autoptr works */
#ifdef __GNUC__
g_assert_null (tac1);
g_assert_null (tac2);
#endif
g_assert_nonnull (tac3);
g_clear_object (&tac3);
g_assert_null (tac3);
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autoslist cleanup functions (defined using the ones of the base type declared
* with G_DECLARE_DERIVABLE_TYPE) and so that can be used with g_autoslist, and
* that freeing the slist correctly unrefs the object too */
static void
test_autoslist (void)
{
TestAutoCleanup *tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac2 = test_auto_cleanup_new ();
g_autoptr (TestAutoCleanup) tac3 = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac1), (gpointer *) &tac1);
g_object_add_weak_pointer (G_OBJECT (tac2), (gpointer *) &tac2);
g_object_add_weak_pointer (G_OBJECT (tac3), (gpointer *) &tac3);
{
g_autoslist (TestAutoCleanup) l = NULL;
l = g_slist_prepend (l, tac1);
l = g_slist_prepend (l, tac2);
}
/* Only assert if autoptr works */
#ifdef __GNUC__
g_assert_null (tac1);
g_assert_null (tac2);
#endif
g_assert_nonnull (tac3);
g_clear_object (&tac3);
g_assert_null (tac3);
}
/* Verify that an object declared with G_DECLARE_FINAL_TYPE provides by default
* autoqueue cleanup functions (defined using the ones of the base type declared
* with G_DECLARE_DERIVABLE_TYPE) and so that can be used with g_autoqueue, and
* that freeing the queue correctly unrefs the object too */
static void
test_autoqueue (void)
{
TestAutoCleanup *tac1 = test_auto_cleanup_new ();
TestAutoCleanup *tac2 = test_auto_cleanup_new ();
g_autoptr (TestAutoCleanup) tac3 = test_auto_cleanup_new ();
g_object_add_weak_pointer (G_OBJECT (tac1), (gpointer *) &tac1);
g_object_add_weak_pointer (G_OBJECT (tac2), (gpointer *) &tac2);
g_object_add_weak_pointer (G_OBJECT (tac3), (gpointer *) &tac3);
{
g_autoqueue (TestAutoCleanup) q = g_queue_new ();
g_queue_push_head (q, tac1);
g_queue_push_tail (q, tac2);
}
/* Only assert if autoptr works */
#ifdef __GNUC__
g_assert_null (tac1);
g_assert_null (tac2);
#endif
g_assert_nonnull (tac3);
g_clear_object (&tac3);
g_assert_null (tac3);
}
static void
test_autoclass (void)
{
g_autoptr (TestAutoCleanupBaseClass) base_class_ptr = NULL;
g_autoptr (TestAutoCleanupClass) class_ptr = NULL;
base_class_ptr = g_type_class_ref (test_base_auto_cleanup_get_type ());
class_ptr = g_type_class_ref (test_auto_cleanup_get_type ());
g_assert_nonnull (base_class_ptr);
g_assert_nonnull (class_ptr);
}
int
main (int argc, gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/autoptr/autoptr", test_autoptr);
g_test_add_func ("/autoptr/autoptr_steal", test_autoptr_steal);
g_test_add_func ("/autoptr/autolist", test_autolist);
g_test_add_func ("/autoptr/autoslist", test_autoslist);
g_test_add_func ("/autoptr/autoqueue", test_autoqueue);
g_test_add_func ("/autoptr/autoclass", test_autoclass);
return g_test_run ();
}

1102
gobject/tests/binding.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,694 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* 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.1 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <glib-object.h>
/* Copied from glib */
typedef struct _BindingSource
{
GObject parent_instance;
gint foo;
gint bar;
gdouble value;
gboolean toggle;
} BindingSource;
typedef struct _BindingSourceClass
{
GObjectClass parent_class;
} BindingSourceClass;
enum
{
PROP_SOURCE_FOO = 1,
PROP_SOURCE_BAR,
PROP_SOURCE_VALUE,
PROP_SOURCE_TOGGLE
};
static GType binding_source_get_type (void);
G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
static void
binding_source_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BindingSource *source = (BindingSource *) gobject;
switch (prop_id)
{
case PROP_SOURCE_FOO:
source->foo = g_value_get_int (value);
break;
case PROP_SOURCE_BAR:
source->bar = g_value_get_int (value);
break;
case PROP_SOURCE_VALUE:
source->value = g_value_get_double (value);
break;
case PROP_SOURCE_TOGGLE:
source->toggle = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_source_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BindingSource *source = (BindingSource *) gobject;
switch (prop_id)
{
case PROP_SOURCE_FOO:
g_value_set_int (value, source->foo);
break;
case PROP_SOURCE_BAR:
g_value_set_int (value, source->bar);
break;
case PROP_SOURCE_VALUE:
g_value_set_double (value, source->value);
break;
case PROP_SOURCE_TOGGLE:
g_value_set_boolean (value, source->toggle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_source_class_init (BindingSourceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = binding_source_set_property;
gobject_class->get_property = binding_source_get_property;
g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
g_param_spec_int ("foo", "Foo", "Foo",
-1, 100,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
g_param_spec_int ("bar", "Bar", "Bar",
-1, 100,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
g_param_spec_double ("value", "Value", "Value",
-100.0, 200.0,
0.0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
g_param_spec_boolean ("toggle", "Toggle", "Toggle",
FALSE,
G_PARAM_READWRITE));
}
static void
binding_source_init (BindingSource *self)
{
}
typedef struct _BindingTarget
{
GObject parent_instance;
gint bar;
gdouble value;
gboolean toggle;
} BindingTarget;
typedef struct _BindingTargetClass
{
GObjectClass parent_class;
} BindingTargetClass;
enum
{
PROP_TARGET_BAR = 1,
PROP_TARGET_VALUE,
PROP_TARGET_TOGGLE
};
static GType binding_target_get_type (void);
G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
static void
binding_target_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BindingTarget *target = (BindingTarget *) gobject;
switch (prop_id)
{
case PROP_TARGET_BAR:
target->bar = g_value_get_int (value);
break;
case PROP_TARGET_VALUE:
target->value = g_value_get_double (value);
break;
case PROP_TARGET_TOGGLE:
target->toggle = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_target_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BindingTarget *target = (BindingTarget *) gobject;
switch (prop_id)
{
case PROP_TARGET_BAR:
g_value_set_int (value, target->bar);
break;
case PROP_TARGET_VALUE:
g_value_set_double (value, target->value);
break;
case PROP_TARGET_TOGGLE:
g_value_set_boolean (value, target->toggle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
binding_target_class_init (BindingTargetClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = binding_target_set_property;
gobject_class->get_property = binding_target_get_property;
g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
g_param_spec_int ("bar", "Bar", "Bar",
-1, 100,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TARGET_VALUE,
g_param_spec_double ("value", "Value", "Value",
-100.0, 200.0,
0.0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
g_param_spec_boolean ("toggle", "Toggle", "Toggle",
FALSE,
G_PARAM_READWRITE));
}
static void
binding_target_init (BindingTarget *self)
{
}
static gboolean
celsius_to_fahrenheit (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data G_GNUC_UNUSED)
{
gdouble celsius, fahrenheit;
g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
celsius = g_value_get_double (from_value);
fahrenheit = (9 * celsius / 5) + 32.0;
if (g_test_verbose ())
g_printerr ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
g_value_set_double (to_value, fahrenheit);
return TRUE;
}
static gboolean
fahrenheit_to_celsius (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data G_GNUC_UNUSED)
{
gdouble celsius, fahrenheit;
g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
fahrenheit = g_value_get_double (from_value);
celsius = 5 * (fahrenheit - 32.0) / 9;
if (g_test_verbose ())
g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
g_value_set_double (to_value, celsius);
return TRUE;
}
static void
test_binding_group_invalid (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
/* Invalid Target Property */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*find_property*target_property*!=*NULL*");
g_binding_group_bind (group, "value",
target, "does-not-exist",
G_BINDING_DEFAULT);
g_test_assert_expected_messages ();
g_binding_group_set_source (group, NULL);
/* Invalid Source Property */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*find_property*source_property*!=*NULL*");
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "does-not-exist",
target, "value",
G_BINDING_DEFAULT);
g_test_assert_expected_messages ();
g_binding_group_set_source (group, NULL);
/* Invalid Source */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*find_property*->source_property*!=*NULL*");
g_binding_group_bind (group, "does-not-exist",
target, "value",
G_BINDING_DEFAULT);
g_binding_group_set_source (group, source);
g_test_assert_expected_messages ();
g_object_unref (target);
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_default (void)
{
gsize i, j;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *targets[5];
BindingSource *readback;
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
{
targets[i] = g_object_new (binding_target_get_type (), NULL);
g_binding_group_bind (group, "foo",
targets[i], "bar",
G_BINDING_DEFAULT);
}
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
for (i = 0; i < 2; ++i)
{
g_object_set (source, "foo", 42, NULL);
for (j = 0; j < G_N_ELEMENTS (targets); ++j)
g_assert_cmpint (source->foo, ==, targets[j]->bar);
g_object_set (targets[0], "bar", 47, NULL);
g_assert_cmpint (source->foo, !=, targets[0]->bar);
/* Check that we transition the source correctly */
g_binding_group_set_source (group, NULL);
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
}
g_object_unref (group);
g_object_set (source, "foo", 0, NULL);
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
g_assert_cmpint (source->foo, !=, targets[i]->bar);
g_object_unref (source);
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
g_object_unref (targets[i]);
}
static void
test_binding_group_bidirectional (void)
{
gsize i, j;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *targets[5];
BindingSource *readback;
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
{
targets[i] = g_object_new (binding_target_get_type (), NULL);
g_binding_group_bind (group, "value",
targets[i], "value",
G_BINDING_BIDIRECTIONAL);
}
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
for (i = 0; i < 2; ++i)
{
g_object_set (source, "value", 42.0, NULL);
for (j = 0; j < G_N_ELEMENTS (targets); ++j)
g_assert_cmpfloat (source->value, ==, targets[j]->value);
g_object_set (targets[0], "value", 47.0, NULL);
g_assert_cmpfloat (source->value, ==, targets[0]->value);
/* Check that we transition the source correctly */
g_binding_group_set_source (group, NULL);
g_assert_null (g_binding_group_dup_source (group));
g_binding_group_set_source (group, source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
}
g_object_unref (group);
g_object_set (targets[0], "value", 0.0, NULL);
g_assert_cmpfloat (source->value, !=, targets[0]->value);
g_object_unref (source);
for (i = 0; i < G_N_ELEMENTS (targets); ++i)
g_object_unref (targets[i]);
}
static void
transform_destroy_notify (gpointer data)
{
gboolean *transform_destroy_called = data;
*transform_destroy_called = TRUE;
}
static void
test_binding_group_transform (void)
{
gboolean transform_destroy_called = FALSE;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
g_binding_group_set_source (group, source);
g_binding_group_bind_full (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL,
celsius_to_fahrenheit,
fahrenheit_to_celsius,
&transform_destroy_called,
transform_destroy_notify);
g_object_set (source, "value", 24.0, NULL);
g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
g_object_set (target, "value", 69.0, NULL);
g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
/* The GDestroyNotify should only be called when the
* set is freed, not when the various GBindings are freed
*/
g_binding_group_set_source (group, NULL);
g_assert_false (transform_destroy_called);
g_object_unref (group);
g_assert_true (transform_destroy_called);
g_object_unref (source);
g_object_unref (target);
}
static void
test_binding_group_transform_closures (void)
{
gboolean transform_destroy_called_1 = FALSE;
gboolean transform_destroy_called_2 = FALSE;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
GClosure *c2f_closure, *f2c_closure;
c2f_closure = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit),
&transform_destroy_called_1,
(GClosureNotify) transform_destroy_notify);
f2c_closure = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius),
&transform_destroy_called_2,
(GClosureNotify) transform_destroy_notify);
g_binding_group_set_source (group, source);
g_binding_group_bind_with_closures (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL,
c2f_closure,
f2c_closure);
g_object_set (source, "value", 24.0, NULL);
g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
g_object_set (target, "value", 69.0, NULL);
g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
/* The GClsoureNotify should only be called when the
* set is freed, not when the various GBindings are freed
*/
g_binding_group_set_source (group, NULL);
g_assert_false (transform_destroy_called_1);
g_assert_false (transform_destroy_called_2);
g_object_unref (group);
g_assert_true (transform_destroy_called_1);
g_assert_true (transform_destroy_called_2);
g_object_unref (source);
g_object_unref (target);
}
static void
test_binding_group_same_object (void)
{
gsize i;
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (),
"foo", 100,
"bar", 50,
NULL);
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "foo",
source, "bar",
G_BINDING_BIDIRECTIONAL);
for (i = 0; i < 2; ++i)
{
g_object_set (source, "foo", 10, NULL);
g_assert_cmpint (source->foo, ==, 10);
g_assert_cmpint (source->bar, ==, 10);
g_object_set (source, "bar", 30, NULL);
g_assert_cmpint (source->foo, ==, 30);
g_assert_cmpint (source->bar, ==, 30);
/* Check that it is possible both when initially
* adding the binding and when changing the source
*/
g_binding_group_set_source (group, NULL);
g_binding_group_set_source (group, source);
}
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_weak_ref_source (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
BindingSource *readback;
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL);
g_object_add_weak_pointer (G_OBJECT (source), (gpointer)&source);
readback = g_binding_group_dup_source (group);
g_assert_true (readback == source);
g_object_unref (readback);
g_object_unref (source);
g_assert_null (source);
g_assert_null (g_binding_group_dup_source (group));
/* Hopefully this would explode if the binding was still alive */
g_object_set (target, "value", 0.0, NULL);
g_object_unref (target);
g_object_unref (group);
}
static void
test_binding_group_weak_ref_target (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL);
g_object_set (source, "value", 47.0, NULL);
g_assert_cmpfloat (target->value, ==, 47.0);
g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
g_object_unref (target);
g_assert_null (target);
/* Hopefully this would explode if the binding was still alive */
g_object_set (source, "value", 0.0, NULL);
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_properties (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
BindingSource *other;
g_binding_group_set_source (group, source);
g_binding_group_bind (group, "value",
target, "value",
G_BINDING_BIDIRECTIONAL);
g_object_get (group, "source", &other, NULL);
g_assert_true (other == source);
g_object_unref (other);
g_object_set (group, "source", NULL, NULL);
g_object_get (group, "source", &other, NULL);
g_assert_null (other);
g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
g_object_unref (target);
g_assert_null (target);
g_object_unref (source);
g_object_unref (group);
}
static void
test_binding_group_weak_notify_no_bindings (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
g_binding_group_set_source (group, source);
g_assert_finalize_object (source);
g_assert_finalize_object (group);
}
static void
test_binding_group_empty_closures (void)
{
GBindingGroup *group = g_binding_group_new ();
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
g_binding_group_bind_full (group, "value", target, "value", 0,
NULL, NULL, NULL, NULL);
g_assert_finalize_object (group);
g_assert_finalize_object (target);
g_assert_finalize_object (source);
}
gint
main (gint argc,
gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/GObject/BindingGroup/invalid", test_binding_group_invalid);
g_test_add_func ("/GObject/BindingGroup/default", test_binding_group_default);
g_test_add_func ("/GObject/BindingGroup/bidirectional", test_binding_group_bidirectional);
g_test_add_func ("/GObject/BindingGroup/transform", test_binding_group_transform);
g_test_add_func ("/GObject/BindingGroup/transform-closures", test_binding_group_transform_closures);
g_test_add_func ("/GObject/BindingGroup/same-object", test_binding_group_same_object);
g_test_add_func ("/GObject/BindingGroup/weak-ref-source", test_binding_group_weak_ref_source);
g_test_add_func ("/GObject/BindingGroup/weak-ref-target", test_binding_group_weak_ref_target);
g_test_add_func ("/GObject/BindingGroup/properties", test_binding_group_properties);
g_test_add_func ("/GObject/BindingGroup/weak-notify-no-bindings", test_binding_group_weak_notify_no_bindings);
g_test_add_func ("/GObject/BindingGroup/empty-closures", test_binding_group_empty_closures);
return g_test_run ();
}

704
gobject/tests/boxed.c Normal file
View file

@ -0,0 +1,704 @@
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include <glib-object.h>
typedef struct _MyBoxed MyBoxed;
struct _MyBoxed
{
gint ivalue;
gchar *bla;
};
static gpointer
my_boxed_copy (gpointer orig)
{
MyBoxed *a = orig;
MyBoxed *b;
b = g_slice_new (MyBoxed);
b->ivalue = a->ivalue;
b->bla = g_strdup (a->bla);
return b;
}
static gint my_boxed_free_count;
static void
my_boxed_free (gpointer orig)
{
MyBoxed *a = orig;
g_free (a->bla);
g_slice_free (MyBoxed, a);
my_boxed_free_count++;
}
static GType my_boxed_get_type (void);
#define MY_TYPE_BOXED (my_boxed_get_type ())
G_DEFINE_BOXED_TYPE (MyBoxed, my_boxed, my_boxed_copy, my_boxed_free)
static void
test_define_boxed (void)
{
MyBoxed a;
MyBoxed *b;
a.ivalue = 20;
a.bla = g_strdup ("bla");
b = g_boxed_copy (MY_TYPE_BOXED, &a);
g_assert_cmpint (b->ivalue, ==, 20);
g_assert_cmpstr (b->bla, ==, "bla");
g_boxed_free (MY_TYPE_BOXED, b);
g_free (a.bla);
}
static void
test_boxed_ownership (void)
{
GValue value = G_VALUE_INIT;
static MyBoxed boxed = { 10, "bla" };
g_value_init (&value, MY_TYPE_BOXED);
my_boxed_free_count = 0;
g_value_set_static_boxed (&value, &boxed);
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 0);
g_value_set_boxed_take_ownership (&value, g_boxed_copy (MY_TYPE_BOXED, &boxed));
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 1);
g_value_take_boxed (&value, g_boxed_copy (MY_TYPE_BOXED, &boxed));
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 2);
g_value_set_boxed (&value, &boxed);
g_value_reset (&value);
g_assert_cmpint (my_boxed_free_count, ==, 3);
}
static void
my_callback (gpointer user_data)
{
}
static gint destroy_count;
static void
my_closure_notify (gpointer user_data, GClosure *closure)
{
destroy_count++;
}
static void
test_boxed_closure (void)
{
GClosure *closure;
GClosure *closure2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_CLOSURE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
closure = g_cclosure_new (G_CALLBACK (my_callback), "bla", my_closure_notify);
g_value_take_boxed (&value, closure);
closure2 = g_value_get_boxed (&value);
g_assert (closure2 == closure);
closure2 = g_value_dup_boxed (&value);
g_assert (closure2 == closure); /* closures use ref/unref for copy/free */
g_closure_unref (closure2);
g_value_unset (&value);
g_assert_cmpint (destroy_count, ==, 1);
}
static void
test_boxed_date (void)
{
GDate *date;
GDate *date2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_DATE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
date = g_date_new_dmy (1, 3, 1970);
g_value_take_boxed (&value, date);
date2 = g_value_get_boxed (&value);
g_assert (date2 == date);
date2 = g_value_dup_boxed (&value);
g_assert (date2 != date);
g_assert (g_date_compare (date, date2) == 0);
g_date_free (date2);
g_value_unset (&value);
}
static void
test_boxed_value (void)
{
GValue value1 = G_VALUE_INIT;
GValue *value2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_VALUE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
g_value_init (&value1, G_TYPE_INT);
g_value_set_int (&value1, 26);
g_value_set_static_boxed (&value, &value1);
value2 = g_value_get_boxed (&value);
g_assert (value2 == &value1);
value2 = g_value_dup_boxed (&value);
g_assert (value2 != &value1);
g_assert (G_VALUE_HOLDS_INT (value2));
g_assert_cmpint (g_value_get_int (value2), ==, 26);
g_boxed_free (G_TYPE_VALUE, value2);
g_value_unset (&value);
}
static void
test_boxed_string (void)
{
GString *v;
GString *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_GSTRING);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_string_new ("bla");
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 != v);
g_assert (g_string_equal (v, v2));
g_string_free (v2, TRUE);
g_value_unset (&value);
}
static void
test_boxed_hashtable (void)
{
GHashTable *v;
GHashTable *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_HASH_TABLE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_hash_table_new (g_str_hash, g_str_equal);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* hash tables use ref/unref for copy/free */
g_hash_table_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_array (void)
{
GArray *v;
GArray *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_ARRAY);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_array_new (TRUE, FALSE, 1);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* arrays use ref/unref for copy/free */
g_array_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_ptrarray (void)
{
GPtrArray *v;
GPtrArray *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_PTR_ARRAY);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_ptr_array_new ();
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* ptr arrays use ref/unref for copy/free */
g_ptr_array_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_regex (void)
{
GRegex *v;
GRegex *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_REGEX);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_regex_new ("a+b+", 0, 0, NULL);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* regexes use ref/unref for copy/free */
g_regex_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_matchinfo (void)
{
GRegex *r;
GMatchInfo *info, *info2;
gboolean ret;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MATCH_INFO);
g_assert (G_VALUE_HOLDS_BOXED (&value));
r = g_regex_new ("ab", 0, 0, NULL);
ret = g_regex_match (r, "blabla abab bla", 0, &info);
g_assert (ret);
g_value_take_boxed (&value, info);
info2 = g_value_get_boxed (&value);
g_assert (info == info2);
info2 = g_value_dup_boxed (&value);
g_assert (info == info2); /* matchinfo uses ref/unref for copy/free */
g_match_info_unref (info2);
g_value_unset (&value);
g_regex_unref (r);
}
static void
test_boxed_varianttype (void)
{
GVariantType *v;
GVariantType *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_VARIANT_TYPE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_variant_type_new ("mas");
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 != v);
g_assert_cmpstr (g_variant_type_peek_string (v), ==, g_variant_type_peek_string (v2));
g_variant_type_free (v2);
g_value_unset (&value);
}
static void
test_boxed_datetime (void)
{
GDateTime *v;
GDateTime *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_DATE_TIME);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_date_time_new_now_local ();
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 == v); /* datetime uses ref/unref for copy/free */
g_date_time_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_error (void)
{
GError *v;
GError *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_ERROR);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_error_new_literal (G_VARIANT_PARSE_ERROR,
G_VARIANT_PARSE_ERROR_NUMBER_TOO_BIG,
"Too damn big");
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v2 == v);
v2 = g_value_dup_boxed (&value);
g_assert (v2 != v);
g_assert_cmpint (v->domain, ==, v2->domain);
g_assert_cmpint (v->code, ==, v2->code);
g_assert_cmpstr (v->message, ==, v2->message);
g_error_free (v2);
g_value_unset (&value);
}
static void
test_boxed_keyfile (void)
{
GKeyFile *k, *k2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_KEY_FILE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
k = g_key_file_new ();
g_value_take_boxed (&value, k);
k2 = g_value_get_boxed (&value);
g_assert (k == k2);
k2 = g_value_dup_boxed (&value);
g_assert (k == k2); /* keyfile uses ref/unref for copy/free */
g_key_file_unref (k2);
g_value_unset (&value);
}
static void
test_boxed_mainloop (void)
{
GMainLoop *l, *l2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MAIN_LOOP);
g_assert (G_VALUE_HOLDS_BOXED (&value));
l = g_main_loop_new (NULL, FALSE);
g_value_take_boxed (&value, l);
l2 = g_value_get_boxed (&value);
g_assert (l == l2);
l2 = g_value_dup_boxed (&value);
g_assert (l == l2); /* mainloop uses ref/unref for copy/free */
g_main_loop_unref (l2);
g_value_unset (&value);
}
static void
test_boxed_maincontext (void)
{
GMainContext *c, *c2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MAIN_CONTEXT);
g_assert (G_VALUE_HOLDS_BOXED (&value));
c = g_main_context_new ();
g_value_take_boxed (&value, c);
c2 = g_value_get_boxed (&value);
g_assert (c == c2);
c2 = g_value_dup_boxed (&value);
g_assert (c == c2); /* maincontext uses ref/unref for copy/free */
g_main_context_unref (c2);
g_value_unset (&value);
}
static void
test_boxed_source (void)
{
GSource *s, *s2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_SOURCE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
s = g_idle_source_new ();
g_value_take_boxed (&value, s);
s2 = g_value_get_boxed (&value);
g_assert (s == s2);
s2 = g_value_dup_boxed (&value);
g_assert (s == s2); /* source uses ref/unref for copy/free */
g_source_unref (s2);
g_value_unset (&value);
}
static void
test_boxed_variantbuilder (void)
{
GVariantBuilder *v, *v2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_VARIANT_BUILDER);
g_assert (G_VALUE_HOLDS_BOXED (&value));
v = g_variant_builder_new (G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
g_value_take_boxed (&value, v);
v2 = g_value_get_boxed (&value);
g_assert (v == v2);
v2 = g_value_dup_boxed (&value);
g_assert (v == v2); /* variantbuilder uses ref/unref for copy/free */
g_variant_builder_unref (v2);
g_value_unset (&value);
}
static void
test_boxed_timezone (void)
{
GTimeZone *z, *z2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_TIME_ZONE);
g_assert (G_VALUE_HOLDS_BOXED (&value));
z = g_time_zone_new_utc ();
g_value_take_boxed (&value, z);
z2 = g_value_get_boxed (&value);
g_assert (z == z2);
z2 = g_value_dup_boxed (&value);
g_assert (z == z2); /* timezone uses ref/unref for copy/free */
g_time_zone_unref (z2);
g_value_unset (&value);
}
static void
test_boxed_pollfd (void)
{
GPollFD *p, *p2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_POLLFD);
g_assert (G_VALUE_HOLDS_BOXED (&value));
p = g_new (GPollFD, 1);
g_value_take_boxed (&value, p);
p2 = g_value_get_boxed (&value);
g_assert (p == p2);
p2 = g_value_dup_boxed (&value);
g_assert (p != p2);
g_free (p2);
g_value_unset (&value);
}
static void
test_boxed_markup (void)
{
GMarkupParseContext *c, *c2;
const GMarkupParser parser = { 0 };
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_MARKUP_PARSE_CONTEXT);
g_assert (G_VALUE_HOLDS_BOXED (&value));
c = g_markup_parse_context_new (&parser, 0, NULL, NULL);
g_value_take_boxed (&value, c);
c2 = g_value_get_boxed (&value);
g_assert (c == c2);
c2 = g_value_dup_boxed (&value);
g_assert (c == c2);
g_markup_parse_context_unref (c2);
g_value_unset (&value);
}
static void
test_boxed_thread (void)
{
GThread *t, *t2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_THREAD);
g_assert (G_VALUE_HOLDS_BOXED (&value));
t = g_thread_self ();
g_value_set_boxed (&value, t);
t2 = g_value_get_boxed (&value);
g_assert (t == t2);
t2 = g_value_dup_boxed (&value);
g_assert (t == t2);
g_thread_unref (t2);
g_value_unset (&value);
}
static void
test_boxed_checksum (void)
{
GChecksum *c, *c2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_CHECKSUM);
g_assert (G_VALUE_HOLDS_BOXED (&value));
c = g_checksum_new (G_CHECKSUM_SHA512);
g_value_take_boxed (&value, c);
c2 = g_value_get_boxed (&value);
g_assert (c == c2);
c2 = g_value_dup_boxed (&value);
g_assert (c != c2);
g_checksum_free (c2);
g_value_unset (&value);
}
static gint
treecmp (gconstpointer a, gconstpointer b)
{
return (a < b) ? -1 : (a > b);
}
static void
test_boxed_tree (void)
{
GTree *t, *t2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_TREE);
g_assert_true (G_VALUE_HOLDS_BOXED (&value));
t = g_tree_new (treecmp);
g_value_take_boxed (&value, t);
t2 = g_value_get_boxed (&value);
g_assert_true (t == t2);
t2 = g_value_dup_boxed (&value);
g_assert_true (t == t2); /* trees use ref/unref for copy/free */
g_tree_unref (t2);
g_value_unset (&value);
}
static void
test_boxed_pattern_spec (void)
{
GPatternSpec *ps, *ps2;
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_PATTERN_SPEC);
g_assert_true (G_VALUE_HOLDS_BOXED (&value));
ps = g_pattern_spec_new ("*abc*?cde");
g_value_take_boxed (&value, ps);
ps2 = g_value_get_boxed (&value);
g_assert_true (ps == ps2);
ps2 = g_value_dup_boxed (&value);
g_assert_true (ps != ps2);
g_assert_true (g_pattern_spec_equal (ps, ps2));
g_pattern_spec_free (ps2);
g_value_unset (&value);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/boxed/define", test_define_boxed);
g_test_add_func ("/boxed/ownership", test_boxed_ownership);
g_test_add_func ("/boxed/closure", test_boxed_closure);
g_test_add_func ("/boxed/date", test_boxed_date);
g_test_add_func ("/boxed/value", test_boxed_value);
g_test_add_func ("/boxed/string", test_boxed_string);
g_test_add_func ("/boxed/hashtable", test_boxed_hashtable);
g_test_add_func ("/boxed/array", test_boxed_array);
g_test_add_func ("/boxed/ptrarray", test_boxed_ptrarray);
g_test_add_func ("/boxed/regex", test_boxed_regex);
g_test_add_func ("/boxed/varianttype", test_boxed_varianttype);
g_test_add_func ("/boxed/error", test_boxed_error);
g_test_add_func ("/boxed/datetime", test_boxed_datetime);
g_test_add_func ("/boxed/matchinfo", test_boxed_matchinfo);
g_test_add_func ("/boxed/keyfile", test_boxed_keyfile);
g_test_add_func ("/boxed/mainloop", test_boxed_mainloop);
g_test_add_func ("/boxed/maincontext", test_boxed_maincontext);
g_test_add_func ("/boxed/source", test_boxed_source);
g_test_add_func ("/boxed/variantbuilder", test_boxed_variantbuilder);
g_test_add_func ("/boxed/timezone", test_boxed_timezone);
g_test_add_func ("/boxed/pollfd", test_boxed_pollfd);
g_test_add_func ("/boxed/markup", test_boxed_markup);
g_test_add_func ("/boxed/thread", test_boxed_thread);
g_test_add_func ("/boxed/checksum", test_boxed_checksum);
g_test_add_func ("/boxed/tree", test_boxed_tree);
g_test_add_func ("/boxed/patternspec", test_boxed_pattern_spec);
return g_test_run ();
}

View file

@ -0,0 +1,337 @@
/* Copyright (C) 2005 Imendio AB
*
* This software is provided "as is"; redistribution and modification
* is permitted, provided that the following disclaimer is retained.
*
* This software 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.
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#endif
#define TEST_POINTER1 ((gpointer) 47)
#define TEST_POINTER2 ((gpointer) 49)
#define TEST_INT1 (-77)
#define TEST_INT2 (78)
/* --- GTest class --- */
typedef struct {
GObject object;
gint value;
gpointer test_pointer1;
gpointer test_pointer2;
} GTest;
typedef struct {
GObjectClass parent_class;
void (*test_signal1) (GTest * test, gint an_int);
void (*test_signal2) (GTest * test, gint an_int);
} GTestClass;
#define G_TYPE_TEST (my_test_get_type ())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
static GType my_test_get_type (void);
G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
/* Test state */
typedef struct
{
GClosure *closure; /* (unowned) */
gboolean stopping;
gboolean seen_signal_handler;
gboolean seen_cleanup;
gboolean seen_test_int1;
gboolean seen_test_int2;
gboolean seen_thread1;
gboolean seen_thread2;
} TestClosureRefcountData;
/* --- functions --- */
static void
my_test_init (GTest * test)
{
g_test_message ("Init %p", test);
test->value = 0;
test->test_pointer1 = TEST_POINTER1;
test->test_pointer2 = TEST_POINTER2;
}
typedef enum
{
PROP_TEST_PROP = 1,
} MyTestProperty;
typedef enum
{
SIGNAL_TEST_SIGNAL1,
SIGNAL_TEST_SIGNAL2,
} MyTestSignal;
static guint signals[SIGNAL_TEST_SIGNAL2 + 1] = { 0, };
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_TEST_PROP:
test->value = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_TEST_PROP:
g_value_set_int (value, test->value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_test_signal2 (GTest *test,
gint an_int)
{
}
static void
my_test_emit_test_signal1 (GTest *test,
gint vint)
{
g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL1], 0, vint);
}
static void
my_test_emit_test_signal2 (GTest *test,
gint vint)
{
g_signal_emit (G_OBJECT (test), signals[SIGNAL_TEST_SIGNAL2], 0, vint);
}
static void
my_test_class_init (GTestClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = my_test_set_property;
gobject_class->get_property = my_test_get_property;
signals[SIGNAL_TEST_SIGNAL1] =
g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GTestClass, test_signal1), NULL, NULL,
g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
signals[SIGNAL_TEST_SIGNAL2] =
g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GTestClass, test_signal2), NULL, NULL,
g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TEST_PROP,
g_param_spec_int ("test-prop", "Test Prop", "Test property",
0, 1, 0, G_PARAM_READWRITE));
klass->test_signal2 = my_test_test_signal2;
}
static void
test_closure (GClosure *closure)
{
/* try to produce high contention in closure->ref_count */
guint i = 0, n = g_random_int () % 199;
for (i = 0; i < n; i++)
g_closure_ref (closure);
g_closure_sink (closure); /* NOP */
for (i = 0; i < n; i++)
g_closure_unref (closure);
}
static gpointer
thread1_main (gpointer user_data)
{
TestClosureRefcountData *data = user_data;
guint i = 0;
for (i = 1; !g_atomic_int_get (&data->stopping); i++)
{
test_closure (data->closure);
if (i % 10000 == 0)
{
g_test_message ("Yielding from thread1");
g_thread_yield (); /* force context switch */
g_atomic_int_set (&data->seen_thread1, TRUE);
}
}
return NULL;
}
static gpointer
thread2_main (gpointer user_data)
{
TestClosureRefcountData *data = user_data;
guint i = 0;
for (i = 1; !g_atomic_int_get (&data->stopping); i++)
{
test_closure (data->closure);
if (i % 10000 == 0)
{
g_test_message ("Yielding from thread2");
g_thread_yield (); /* force context switch */
g_atomic_int_set (&data->seen_thread2, TRUE);
}
}
return NULL;
}
static void
test_signal_handler (GTest *test,
gint vint,
gpointer user_data)
{
TestClosureRefcountData *data = user_data;
g_assert_true (test->test_pointer1 == TEST_POINTER1);
data->seen_signal_handler = TRUE;
data->seen_test_int1 |= vint == TEST_INT1;
data->seen_test_int2 |= vint == TEST_INT2;
}
static void
destroy_data (gpointer user_data,
GClosure *closure)
{
TestClosureRefcountData *data = user_data;
data->seen_cleanup = TRUE;
g_assert_true (data->closure == closure);
g_assert_cmpint (closure->ref_count, ==, 0);
}
static void
test_emissions (GTest *test)
{
my_test_emit_test_signal1 (test, TEST_INT1);
my_test_emit_test_signal2 (test, TEST_INT2);
}
/* Test that closure refcounting works even when high contested between three
* threads (the main thread, thread1 and thread2). Both child threads are
* contesting refs/unrefs, while the main thread periodically emits signals
* which also do refs/unrefs on closures. */
static void
test_closure_refcount (void)
{
GThread *thread1, *thread2;
TestClosureRefcountData test_data = { 0, };
GClosure *closure;
GTest *object;
guint i, n_iterations;
object = g_object_new (G_TYPE_TEST, NULL);
closure = g_cclosure_new (G_CALLBACK (test_signal_handler), &test_data, destroy_data);
g_signal_connect_closure (object, "test-signal1", closure, FALSE);
g_signal_connect_closure (object, "test-signal2", closure, FALSE);
test_data.stopping = FALSE;
test_data.closure = closure;
thread1 = g_thread_new ("thread1", thread1_main, &test_data);
thread2 = g_thread_new ("thread2", thread2_main, &test_data);
/* The 16-bit compare-and-swap operations currently used for closure
* refcounts are really slow on some ARM CPUs, notably Cortex-A57.
* Reduce the number of iterations so that the test completes in a
* finite time, but don't reduce it so much that the main thread
* starves the other threads and causes a test failure.
*
* https://gitlab.gnome.org/GNOME/glib/issues/1316
* aka https://bugs.debian.org/880883 */
#if defined(__aarch64__) || defined(__arm__)
n_iterations = 100000;
#else
n_iterations = 1000000;
#endif
/* Run the test for a reasonably high number of iterations, and ensure we
* dont terminate until at least 10000 iterations have completed in both
* thread1 and thread2. Even though @n_iterations is high, we cant guarantee
* that the scheduler allocates time fairly (or at all!) to thread1 or
* thread2. */
for (i = 1;
i < n_iterations ||
!g_atomic_int_get (&test_data.seen_thread1) ||
!g_atomic_int_get (&test_data.seen_thread2);
i++)
{
test_emissions (object);
if (i % 10000 == 0)
{
g_test_message ("Yielding from main thread");
g_thread_yield (); /* force context switch */
}
}
g_atomic_int_set (&test_data.stopping, TRUE);
g_test_message ("Stopping");
/* wait for thread shutdown */
g_thread_join (thread1);
g_thread_join (thread2);
/* finalize object, destroy signals, run cleanup code */
g_object_unref (object);
g_test_message ("Stopped");
g_assert_true (g_atomic_int_get (&test_data.seen_thread1));
g_assert_true (g_atomic_int_get (&test_data.seen_thread2));
g_assert_true (test_data.seen_test_int1);
g_assert_true (test_data.seen_test_int2);
g_assert_true (test_data.seen_signal_handler);
g_assert_true (test_data.seen_cleanup);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/closure/refcount", test_closure_refcount);
return g_test_run ();
}

223
gobject/tests/closure.c Normal file
View file

@ -0,0 +1,223 @@
#include <glib-object.h>
#ifdef G_OS_UNIX
#include <glib-unix.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#endif
static void
test_source (GSource *one, GCallback quit_callback)
{
GClosure *closure;
GMainLoop *loop;
/* Callback with GMainLoop user_data */
loop = g_main_loop_new (NULL, FALSE);
closure = g_cclosure_new (quit_callback, loop, NULL);
g_source_set_closure (one, closure);
g_source_attach (one, NULL);
g_main_loop_run (loop);
g_source_destroy (one);
g_main_loop_unref (loop);
}
static gboolean
simple_quit_callback (gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_idle (void)
{
GSource *source;
source = g_idle_source_new ();
test_source (source, G_CALLBACK (simple_quit_callback));
g_source_unref (source);
}
static void
test_closure_timeout (void)
{
GSource *source;
source = g_timeout_source_new (10);
test_source (source, G_CALLBACK (simple_quit_callback));
g_source_unref (source);
}
static gboolean
iochannel_quit_callback (GIOChannel *channel,
GIOCondition cond,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_iochannel (void)
{
GIOChannel *chan;
GSource *source;
char *path;
GError *error = NULL;
if (g_path_is_absolute (g_get_prgname ()))
path = g_strdup (g_get_prgname ());
else
{
path = g_test_build_filename (G_TEST_BUILT,
g_get_prgname (),
NULL);
}
chan = g_io_channel_new_file (path, "r", &error);
g_assert_no_error (error);
g_free (path);
source = g_io_create_watch (chan, G_IO_IN);
test_source (source, G_CALLBACK (iochannel_quit_callback));
g_source_unref (source);
g_io_channel_unref (chan);
}
static void
test_closure_child (void)
{
GSource *source;
GPid pid;
GError *error = NULL;
gchar *argv[3];
g_assert (g_getenv ("DO_NOT_ACCIDENTALLY_RECURSE") == NULL);
g_setenv ("DO_NOT_ACCIDENTALLY_RECURSE", "1", TRUE);
if (g_path_is_absolute (g_get_prgname ()))
argv[0] = g_strdup (g_get_prgname ());
else
{
argv[0] = g_test_build_filename (G_TEST_BUILT,
g_get_prgname (),
NULL);
}
argv[1] = "-l";
argv[2] = NULL;
g_spawn_async (NULL, argv, NULL,
G_SPAWN_STDOUT_TO_DEV_NULL |
G_SPAWN_STDERR_TO_DEV_NULL |
G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL,
&pid, &error);
g_assert_no_error (error);
g_free (argv[0]);
source = g_child_watch_source_new (pid);
test_source (source, G_CALLBACK (iochannel_quit_callback));
g_source_unref (source);
}
#ifdef G_OS_UNIX
static gboolean
fd_quit_callback (gint fd,
GIOCondition condition,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_fd (void)
{
gint fd;
GSource *source;
fd = open ("/dev/null", O_RDONLY);
g_assert (fd != -1);
source = g_unix_fd_source_new (fd, G_IO_IN);
test_source (source, G_CALLBACK (fd_quit_callback));
g_source_unref (source);
close (fd);
}
static gboolean
send_usr1 (gpointer user_data)
{
kill (getpid (), SIGUSR1);
return FALSE;
}
static gboolean
closure_quit_callback (gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return TRUE;
}
static void
test_closure_signal (void)
{
GSource *source;
g_idle_add_full (G_PRIORITY_LOW, send_usr1, NULL, NULL);
source = g_unix_signal_source_new (SIGUSR1);
test_source (source, G_CALLBACK (closure_quit_callback));
g_source_unref (source);
}
#endif
int
main (int argc,
char *argv[])
{
#ifndef G_OS_WIN32
sigset_t sig_mask, old_mask;
sigemptyset (&sig_mask);
sigaddset (&sig_mask, SIGUSR1);
if (sigprocmask (SIG_UNBLOCK, &sig_mask, &old_mask) == 0)
{
if (sigismember (&old_mask, SIGUSR1))
g_message ("SIGUSR1 was blocked, unblocking it");
}
#endif
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/closure/idle", test_closure_idle);
g_test_add_func ("/closure/timeout", test_closure_timeout);
g_test_add_func ("/closure/iochannel", test_closure_iochannel);
g_test_add_func ("/closure/child", test_closure_child);
#ifdef G_OS_UNIX
g_test_add_func ("/closure/fd", test_closure_fd);
g_test_add_func ("/closure/signal", test_closure_signal);
#endif
return g_test_run ();
}

26
gobject/tests/cxx.cpp Normal file
View file

@ -0,0 +1,26 @@
/* Copyright (C) 2001 Sebastian Wilhelmi <wilhelmi@google.com>
*
* 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.1 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/>.
*/
/* A trivial C++ program to be compiled in C++ mode, which
* smoketests that the GObject headers are valid C++ headers. */
#include <glib-object.h>
int
main ()
{
return 0;
}

View file

@ -0,0 +1,368 @@
/* GLib testing framework examples and tests
* Copyright (C) 2008 Imendio AB
* Authors: Tim Janik
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#include <glib.h>
#include <glib-object.h>
/* This test tests the macros for defining dynamic types.
*/
static GMutex sync_mutex;
static gboolean loaded = FALSE;
/* MODULE */
typedef struct _TestModule TestModule;
typedef struct _TestModuleClass TestModuleClass;
#define TEST_TYPE_MODULE (test_module_get_type ())
#define TEST_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), TEST_TYPE_MODULE, TestModule))
#define TEST_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), TEST_TYPE_MODULE, TestModuleClass))
#define TEST_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), TEST_TYPE_MODULE))
#define TEST_IS_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), TEST_TYPE_MODULE))
#define TEST_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), TEST_TYPE_MODULE, TestModuleClass))
typedef void (*TestModuleRegisterFunc) (GTypeModule *module);
struct _TestModule
{
GTypeModule parent_instance;
TestModuleRegisterFunc register_func;
};
struct _TestModuleClass
{
GTypeModuleClass parent_class;
};
static GType test_module_get_type (void);
static gboolean
test_module_load (GTypeModule *module)
{
TestModule *test_module = TEST_MODULE (module);
test_module->register_func (module);
return TRUE;
}
static void
test_module_unload (GTypeModule *module)
{
}
static void
test_module_class_init (TestModuleClass *class)
{
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
module_class->load = test_module_load;
module_class->unload = test_module_unload;
}
static GType test_module_get_type (void)
{
static GType object_type = 0;
if (!object_type) {
static const GTypeInfo object_info =
{
sizeof (TestModuleClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) test_module_class_init,
(GClassFinalizeFunc) NULL,
NULL,
sizeof (TestModule),
0,
(GInstanceInitFunc)NULL,
NULL,
};
object_type = g_type_register_static (G_TYPE_TYPE_MODULE, "TestModule", &object_info, 0);
}
return object_type;
}
static GTypeModule *
test_module_new (TestModuleRegisterFunc register_func)
{
TestModule *test_module = g_object_new (TEST_TYPE_MODULE, NULL);
GTypeModule *module = G_TYPE_MODULE (test_module);
test_module->register_func = register_func;
/* Register the types initially */
g_type_module_use (module);
g_type_module_unuse (module);
return G_TYPE_MODULE (module);
}
#define DYNAMIC_OBJECT_TYPE (dynamic_object_get_type ())
typedef GObject DynamicObject;
typedef struct _DynamicObjectClass DynamicObjectClass;
struct _DynamicObjectClass
{
GObjectClass parent_class;
guint val;
};
static GType dynamic_object_get_type (void);
G_DEFINE_DYNAMIC_TYPE(DynamicObject, dynamic_object, G_TYPE_OBJECT)
static void
dynamic_object_class_init (DynamicObjectClass *class)
{
class->val = 42;
g_assert (loaded == FALSE);
loaded = TRUE;
}
static void
dynamic_object_class_finalize (DynamicObjectClass *class)
{
g_assert (loaded == TRUE);
loaded = FALSE;
}
static void
dynamic_object_init (DynamicObject *dynamic_object)
{
}
static void
module_register (GTypeModule *module)
{
dynamic_object_register_type (module);
}
#define N_THREADS 100
#define N_REFS 10000
static gpointer
ref_unref_thread (gpointer data)
{
gint i;
/* first, synchronize with other threads,
*/
if (g_test_verbose())
g_printerr ("WAITING!\n");
g_mutex_lock (&sync_mutex);
g_mutex_unlock (&sync_mutex);
if (g_test_verbose ())
g_printerr ("STARTING\n");
/* ref/unref the klass 10000000 times */
for (i = N_REFS; i; i--) {
if (g_test_verbose ())
if (i % 10)
g_printerr ("%d\n", i);
g_type_class_unref (g_type_class_ref ((GType) data));
}
if (g_test_verbose())
g_printerr ("DONE !\n");
return NULL;
}
static void
test_multithreaded_dynamic_type_init (void)
{
GTypeModule *module;
DynamicObjectClass *class;
/* Create N_THREADS threads that are going to just ref/unref a class */
GThread *threads[N_THREADS];
guint i;
module = test_module_new (module_register);
g_assert (module != NULL);
/* Not loaded until we call ref for the first time */
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
g_assert (class == NULL);
g_assert (!loaded);
/* pause newly created threads */
g_mutex_lock (&sync_mutex);
/* create threads */
for (i = 0; i < N_THREADS; i++) {
threads[i] = g_thread_new ("test", ref_unref_thread, (gpointer) DYNAMIC_OBJECT_TYPE);
}
/* execute threads */
g_mutex_unlock (&sync_mutex);
for (i = 0; i < N_THREADS; i++) {
g_thread_join (threads[i]);
}
}
enum
{
PROP_0,
PROP_FOO
};
typedef struct _DynObj DynObj;
typedef struct _DynObjClass DynObjClass;
typedef struct _DynIfaceInterface DynIfaceInterface;
struct _DynObj
{
GObject obj;
gint foo;
};
struct _DynObjClass
{
GObjectClass class;
};
struct _DynIfaceInterface
{
GTypeInterface iface;
};
static void dyn_obj_iface_init (DynIfaceInterface *iface);
static GType dyn_iface_get_type (void);
G_DEFINE_INTERFACE (DynIface, dyn_iface, G_TYPE_OBJECT)
static GType dyn_obj_get_type (void);
G_DEFINE_DYNAMIC_TYPE_EXTENDED(DynObj, dyn_obj, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC(dyn_iface_get_type (), dyn_obj_iface_init))
static void
dyn_iface_default_init (DynIfaceInterface *iface)
{
g_object_interface_install_property (iface,
g_param_spec_int ("foo", NULL, NULL, 0, 100, 0, G_PARAM_READWRITE));
}
static void
dyn_obj_iface_init (DynIfaceInterface *iface)
{
}
static void
dyn_obj_init (DynObj *obj)
{
obj->foo = 0;
}
static void
set_prop (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
DynObj *obj = (DynObj *)object;
switch (prop_id)
{
case PROP_FOO:
obj->foo = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_prop (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
DynObj *obj = (DynObj *)object;
switch (prop_id)
{
case PROP_FOO:
g_value_set_int (value, obj->foo);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
dyn_obj_class_init (DynObjClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = set_prop;
object_class->get_property = get_prop;
g_object_class_override_property (object_class, PROP_FOO, "foo");
}
static void
dyn_obj_class_finalize (DynObjClass *class)
{
}
static void
mod_register (GTypeModule *module)
{
dyn_obj_register_type (module);
}
static void
test_dynamic_interface_properties (void)
{
GTypeModule *module;
DynObj *obj;
gint val;
module = test_module_new (mod_register);
g_assert (module != NULL);
obj = g_object_new (dyn_obj_get_type (), "foo", 1, NULL);
g_object_get (obj, "foo", &val, NULL);
g_assert_cmpint (val, ==, 1);
g_object_unref (obj);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/GObject/threaded-dynamic-ref-unref-init", test_multithreaded_dynamic_type_init);
g_test_add_func ("/GObject/dynamic-interface-properties", test_dynamic_interface_properties);
return g_test_run();
}

170
gobject/tests/enums.c Normal file
View file

@ -0,0 +1,170 @@
#include <glib-object.h>
static const GEnumValue my_enum_values[] =
{
{ 1, "the first value", "one" },
{ 2, "the second value", "two" },
{ 3, "the third value", "three" },
{ 0, NULL, NULL }
};
static void
test_enum_basic (void)
{
GType type;
GEnumClass *class;
GEnumValue *val;
GValue value = G_VALUE_INIT;
gchar *to_string;
type = g_enum_register_static ("MyEnum", my_enum_values);
g_value_init (&value, type);
g_assert (G_VALUE_HOLDS_ENUM (&value));
g_value_set_enum (&value, 2);
g_assert_cmpint (g_value_get_enum (&value), ==, 2);
g_value_unset (&value);
class = g_type_class_ref (type);
g_assert_cmpint (class->minimum, ==, 1);
g_assert_cmpint (class->maximum, ==, 3);
g_assert_cmpint (class->n_values, ==, 3);
val = g_enum_get_value (class, 2);
g_assert (val != NULL);
g_assert_cmpstr (val->value_name, ==, "the second value");
val = g_enum_get_value (class, 15);
g_assert (val == NULL);
val = g_enum_get_value_by_name (class, "the third value");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 3);
val = g_enum_get_value_by_name (class, "the color purple");
g_assert (val == NULL);
val = g_enum_get_value_by_nick (class, "one");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 1);
val = g_enum_get_value_by_nick (class, "purple");
g_assert (val == NULL);
to_string = g_enum_to_string (type, 2);
g_assert_cmpstr (to_string, ==, "the second value");
g_free (to_string);
to_string = g_enum_to_string (type, 15);
g_assert_cmpstr (to_string, ==, "15");
g_free (to_string);
g_type_class_unref (class);
}
static const GFlagsValue my_flag_values[] =
{
{ 0, "no flags", "none" },
{ 1, "the first flag", "one" },
{ 2, "the second flag", "two" },
{ 8, "the third flag", "three" },
{ 0, NULL, NULL }
};
static const GFlagsValue no_default_flag_values[] =
{
{ 1, "the first flag", "one" },
{ 0, NULL, NULL }
};
static void
test_flags_transform_to_string (const GValue *value)
{
GValue tmp = G_VALUE_INIT;
g_value_init (&tmp, G_TYPE_STRING);
g_value_transform (value, &tmp);
g_value_unset (&tmp);
}
static void
test_flags_basic (void)
{
GType type, no_default_type;
GFlagsClass *class;
GFlagsValue *val;
GValue value = G_VALUE_INIT;
gchar *to_string;
type = g_flags_register_static ("MyFlags", my_flag_values);
no_default_type = g_flags_register_static ("NoDefaultFlags",
no_default_flag_values);
g_value_init (&value, type);
g_assert (G_VALUE_HOLDS_FLAGS (&value));
g_value_set_flags (&value, 2|8);
g_assert_cmpint (g_value_get_flags (&value), ==, 2|8);
class = g_type_class_ref (type);
g_assert_cmpint (class->mask, ==, 1|2|8);
g_assert_cmpint (class->n_values, ==, 4);
val = g_flags_get_first_value (class, 2|8);
g_assert (val != NULL);
g_assert_cmpstr (val->value_name, ==, "the second flag");
val = g_flags_get_first_value (class, 16);
g_assert (val == NULL);
val = g_flags_get_value_by_name (class, "the third flag");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 8);
val = g_flags_get_value_by_name (class, "the color purple");
g_assert (val == NULL);
val = g_flags_get_value_by_nick (class, "one");
g_assert (val != NULL);
g_assert_cmpint (val->value, ==, 1);
val = g_flags_get_value_by_nick (class, "purple");
g_assert (val == NULL);
test_flags_transform_to_string (&value);
g_value_unset (&value);
to_string = g_flags_to_string (type, 1|8);
g_assert_cmpstr (to_string, ==, "the first flag | the third flag");
g_free (to_string);
to_string = g_flags_to_string (type, 0);
g_assert_cmpstr (to_string, ==, "no flags");
g_free (to_string);
to_string = g_flags_to_string (type, 16);
g_assert_cmpstr (to_string, ==, "0x10");
g_free (to_string);
to_string = g_flags_to_string (type, 1|16);
g_assert_cmpstr (to_string, ==, "the first flag | 0x10");
g_free (to_string);
to_string = g_flags_to_string (no_default_type, 0);
g_assert_cmpstr (to_string, ==, "0x0");
g_free (to_string);
to_string = g_flags_to_string (no_default_type, 16);
g_assert_cmpstr (to_string, ==, "0x10");
g_free (to_string);
g_type_class_unref (class);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/enum/basic", test_enum_basic);
g_test_add_func ("/flags/basic", test_flags_basic);
return g_test_run ();
}

179
gobject/tests/flags.c Normal file
View file

@ -0,0 +1,179 @@
/* flags.c
* Copyright (C) 2018 Arthur Demchenkov
*
* 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.1 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 <glib-object.h>
/* Check that validation of flags works on architectures where
* #gint and #glong are different sizes, as the flags are cast
* between types a few times.
*
* See: https://gitlab.gnome.org/GNOME/glib/issues/1572
*/
enum {
PROP_FLAGS = 1
};
typedef struct _GTest GTest;
typedef struct _GTestClass GTestClass;
typedef enum {
NO_FLAG = 0,
LOWEST_FLAG = 1,
HIGHEST_FLAG = 1 << 31
} MyFlagsEnum;
struct _GTest {
GObject object;
MyFlagsEnum flags;
};
struct _GTestClass {
GObjectClass parent_class;
};
static GType my_test_get_type (void);
static GType my_test_flags_get_type (void);
#define G_TYPE_TEST (my_test_get_type())
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
static void my_test_class_init (GTestClass * klass);
static void my_test_init (GTest * test);
static void my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static GType
my_test_flags_get_type (void)
{
static GType flags_type = 0;
if (G_UNLIKELY(flags_type == 0))
{
static const GFlagsValue values[] = {
{ LOWEST_FLAG, "LOWEST_FLAG", "lowest" },
{ HIGHEST_FLAG, "HIGHEST_FLAG", "highest" },
{ 0, NULL, NULL }
};
flags_type = g_flags_register_static (g_intern_static_string ("GTestFlags"), values);
}
return flags_type;
}
static void
my_test_class_init (GTestClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = my_test_get_property;
gobject_class->set_property = my_test_set_property;
g_object_class_install_property (gobject_class, 1,
g_param_spec_flags ("flags",
"Flags",
"Flags test property",
my_test_flags_get_type(), 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
}
static void my_test_init (GTest *test)
{
}
static void
my_test_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_FLAGS:
g_value_set_flags (value, test->flags);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
my_test_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GTest *test = MY_TEST (object);
switch (prop_id)
{
case PROP_FLAGS:
test->flags = g_value_get_flags (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
check_flags_validation (void)
{
guint test_flags[] = {
NO_FLAG,
LOWEST_FLAG,
HIGHEST_FLAG,
LOWEST_FLAG | HIGHEST_FLAG
};
guint flag_read;
gsize i;
for (i = 0; i < G_N_ELEMENTS (test_flags); i++)
{
guint flag_set = test_flags[i];
GObject *test = g_object_new (G_TYPE_TEST,
"flags", flag_set,
NULL);
g_object_get (test, "flags", &flag_read, NULL);
/* This check will fail in case of gint -> glong conversion
* in value_flags_enum_collect_value() */
g_assert_cmpint (flag_read, ==, flag_set);
g_object_unref (test);
}
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/gobject/flags/validate", check_flags_validation);
return g_test_run ();
}

817
gobject/tests/genmarshal.py Normal file
View file

@ -0,0 +1,817 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright © 2019 Endless Mobile, Inc.
#
# 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.1 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
"""Integration tests for glib-genmarshal utility."""
import collections
import os
import shutil
import subprocess
import sys
import tempfile
from textwrap import dedent
import unittest
import taptestrunner
# Disable line length warnings as wrapping the C code templates would be hard
# flake8: noqa: E501
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestGenmarshal(unittest.TestCase):
"""Integration test for running glib-genmarshal.
This can be run when installed or uninstalled. When uninstalled, it
requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
The idea with this test harness is to test the glib-genmarshal utility, its
handling of command line arguments, its exit statuses, and its handling of
various marshaller lists. In future we could split the core glib-genmarshal
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ""
def setUp(self):
self.timeout_seconds = 10 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__genmarshal = os.path.join(
os.environ["G_TEST_BUILDDIR"], "..", "glib-genmarshal"
)
else:
self.__genmarshal = shutil.which("glib-genmarshal")
print("genmarshal:", self.__genmarshal)
def tearDown(self):
os.chdir(self.cwd)
self.tmpdir.cleanup()
def runGenmarshal(self, *args):
argv = [self.__genmarshal]
# shebang lines are not supported on native
# Windows consoles
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print("Running:", argv)
env = os.environ.copy()
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
"standard_top_comment": "This file is generated by glib-genmarshal, do not modify "
"it. This code is licensed under the same license as the "
"containing project. Note that it links to GLib, so must "
"comply with the LGPL linking clauses.",
"standard_top_pragma": dedent(
"""
#ifndef __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
#define __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__
"""
).strip(),
"standard_bottom_pragma": dedent(
"""
#endif /* __G_CCLOSURE_USER_MARSHAL_MARSHAL_H__ */
"""
).strip(),
"standard_includes": dedent(
"""
#include <glib-object.h>
"""
).strip(),
"standard_marshal_peek_defines": dedent(
"""
#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v)
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
#define g_marshal_value_peek_int(v) g_value_get_int (v)
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
#define g_marshal_value_peek_long(v) g_value_get_long (v)
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
#define g_marshal_value_peek_float(v) g_value_get_float (v)
#define g_marshal_value_peek_double(v) g_value_get_double (v)
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v) g_value_get_param (v)
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
#define g_marshal_value_peek_object(v) g_value_get_object (v)
#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
* Do not access GValues directly in your code. Instead, use the
* g_value_get_*() functions
*/
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */
"""
).strip(),
}
result = Result(info, out, err, subs)
print("Output:", result.out)
return result
def runGenmarshalWithList(self, list_contents, *args):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".list", delete=False
) as list_file:
# Write out the list.
list_file.write(list_contents.encode("utf-8"))
print(list_file.name + ":", list_contents)
list_file.flush()
header_result = self.runGenmarshal(list_file.name, "--header", *args)
body_result = self.runGenmarshal(list_file.name, "--body", *args)
header_result.subs["list_path"] = list_file.name
body_result.subs["list_path"] = list_file.name
return (header_result, body_result)
def test_help(self):
"""Test the --help argument."""
result = self.runGenmarshal("--help")
self.assertIn("usage: glib-genmarshal", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runGenmarshal()
self.assertEqual("", result.err)
self.assertEqual("", result.out)
def test_empty_list(self):
"""Test running with an empty list."""
(header_result, body_result) = self.runGenmarshalWithList("", "--quiet")
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_boolean(self):
"""Test running with a basic VOID:BOOLEAN list."""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:BOOLEAN", "--quiet"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
/* VOID:BOOLEAN ({list_path}:1) */
#define g_cclosure_user_marshal_VOID__BOOLEAN g_cclosure_marshal_VOID__BOOLEAN
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_boolean_int64(self):
"""Test running with a non-trivial VOID:BOOLEAN,INT64 list."""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:BOOLEAN,INT64", "--quiet"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
{standard_includes}
G_BEGIN_DECLS
/* VOID:BOOLEAN,INT64 ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_includes}
{standard_marshal_peek_defines}
/* VOID:BOOLEAN,INT64 ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__BOOLEAN_INT64 (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__BOOLEAN_INT64) (gpointer data1,
gboolean arg1,
gint64 arg2,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__BOOLEAN_INT64 callback;
g_return_if_fail (n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__BOOLEAN_INT64) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_boolean (param_values + 1),
g_marshal_value_peek_int64 (param_values + 2),
data2);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_variant_nostdinc_valist_marshaller(self):
"""Test running with a basic VOID:VARIANT list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for VARIANT correctly sinks floating variants.
See issue #1793.
"""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:VARIANT", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:VARIANT ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:VARIANT ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__VARIANT (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__VARIANT callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_variant (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__VARIANTv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__VARIANT) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__VARIANT callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_variant_ref_sink (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__VARIANT) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_variant_unref (arg0);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_string_nostdinc(self):
"""Test running with a basic VOID:STRING list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for STRING correctly skips a string copy if the
argument is static.
See issue #1792.
"""
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:STRING", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:STRING ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:STRING ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__STRING (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__STRING callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_string (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__STRINGv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__STRING) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__STRING callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_strdup (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__STRING) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_free (arg0);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
def test_void_param_nostdinc(self):
"""Test running with a basic VOID:PARAM list, but without the
standard marshallers, and with valist support enabled. This checks that
the valist marshaller for PARAM correctly skips a param copy if the
argument is static.
See issue #1792.
"""
self.maxDiff = None # TODO
(header_result, body_result) = self.runGenmarshalWithList(
"VOID:PARAM", "--quiet", "--nostdinc", "--valist-marshaller"
)
self.assertEqual("", header_result.err)
self.assertEqual("", body_result.err)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_top_pragma}
G_BEGIN_DECLS
/* VOID:PARAM ({list_path}:1) */
extern
void g_cclosure_user_marshal_VOID__PARAM (GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
extern
void g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
{standard_bottom_pragma}
"""
)
.strip()
.format(**header_result.subs),
header_result.out.strip(),
)
self.assertEqual(
dedent(
"""
/* {standard_top_comment} */
{standard_marshal_peek_defines}
/* VOID:PARAM ({list_path}:1) */
void
g_cclosure_user_marshal_VOID__PARAM (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint G_GNUC_UNUSED,
gpointer marshal_data)
{{
typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__PARAM callback;
g_return_if_fail (n_param_values == 2);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = g_value_peek_pointer (param_values + 0);
}}
else
{{
data1 = g_value_peek_pointer (param_values + 0);
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
callback (data1,
g_marshal_value_peek_param (param_values + 1),
data2);
}}
void
g_cclosure_user_marshal_VOID__PARAMv (GClosure *closure,
GValue *return_value G_GNUC_UNUSED,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{{
typedef void (*GMarshalFunc_VOID__PARAM) (gpointer data1,
gpointer arg1,
gpointer data2);
GCClosure *cc = (GCClosure *) closure;
gpointer data1, data2;
GMarshalFunc_VOID__PARAM callback;
gpointer arg0;
va_list args_copy;
G_VA_COPY (args_copy, args);
arg0 = (gpointer) va_arg (args_copy, gpointer);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
arg0 = g_param_spec_ref (arg0);
va_end (args_copy);
if (G_CCLOSURE_SWAP_DATA (closure))
{{
data1 = closure->data;
data2 = instance;
}}
else
{{
data1 = instance;
data2 = closure->data;
}}
callback = (GMarshalFunc_VOID__PARAM) (marshal_data ? marshal_data : cc->callback);
callback (data1,
arg0,
data2);
if ((param_types[0] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && arg0 != NULL)
g_param_spec_unref (arg0);
}}
"""
)
.strip()
.format(**body_result.subs),
body_result.out.strip(),
)
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

View file

@ -0,0 +1,645 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2001, 2003 Red Hat, Inc.
*
* 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.1 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 <stdlib.h>
#include <string.h>
#include <glib-object.h>
#include "testcommon.h"
/* This test tests interface properties, implementing interface
* properties and #GParamSpecOverride.
*
* Four properties are tested:
*
* prop1: Defined in TestIface, Implemented in BaseObject with a GParamSpecOverride
* prop2: Defined in TestIface, Implemented in BaseObject with a new property
* prop3: Defined in TestIface, Implemented in BaseObject, Overridden in DerivedObject
* prop4: Defined in BaseObject, Overridden in DerivedObject
*/
static GType base_object_get_type (void);
static GType derived_object_get_type (void);
enum {
BASE_PROP_0,
BASE_PROP1,
BASE_PROP2,
BASE_PROP3,
BASE_PROP4
};
enum {
DERIVED_PROP_0,
DERIVED_PROP3,
DERIVED_PROP4
};
/*
* BaseObject, a parent class for DerivedObject
*/
#define BASE_TYPE_OBJECT (base_object_get_type ())
#define BASE_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE_TYPE_OBJECT, BaseObject))
typedef struct _BaseObject BaseObject;
typedef struct _BaseObjectClass BaseObjectClass;
struct _BaseObject
{
GObject parent_instance;
gint val1;
gint val2;
gint val3;
gint val4;
};
struct _BaseObjectClass
{
GObjectClass parent_class;
};
GObjectClass *base_parent_class;
/*
* DerivedObject, the child class of DerivedObject
*/
#define DERIVED_TYPE_OBJECT (derived_object_get_type ())
typedef struct _DerivedObject DerivedObject;
typedef struct _DerivedObjectClass DerivedObjectClass;
struct _DerivedObject
{
BaseObject parent_instance;
};
struct _DerivedObjectClass
{
BaseObjectClass parent_class;
};
/*
* The interface
*/
typedef struct _TestIfaceClass TestIfaceClass;
struct _TestIfaceClass
{
GTypeInterface base_iface;
};
#define TEST_TYPE_IFACE (test_iface_get_type ())
/* The paramspecs installed on our interface
*/
static GParamSpec *iface_spec1, *iface_spec2, *iface_spec3;
/* The paramspecs inherited by our derived object
*/
static GParamSpec *inherited_spec1, *inherited_spec2, *inherited_spec3, *inherited_spec4;
static void
test_iface_default_init (TestIfaceClass *iface_vtable)
{
inherited_spec1 = iface_spec1 = g_param_spec_int ("prop1",
"Prop1",
"Property 1",
G_MININT, /* min */
0xFFFF, /* max */
42, /* default */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
g_object_interface_install_property (iface_vtable, iface_spec1);
iface_spec2 = g_param_spec_int ("prop2",
"Prop2",
"Property 2",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_WRITABLE);
g_object_interface_install_property (iface_vtable, iface_spec2);
inherited_spec3 = iface_spec3 = g_param_spec_int ("prop3",
"Prop3",
"Property 3",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_READWRITE);
g_object_interface_install_property (iface_vtable, iface_spec3);
}
static DEFINE_IFACE (TestIface, test_iface, NULL, test_iface_default_init)
static GObject*
base_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
/* The constructor is the one place where a GParamSpecOverride is visible
* to the outside world, so we do a bunch of checks here
*/
GValue value1 = G_VALUE_INIT;
GValue value2 = G_VALUE_INIT;
GParamSpec *pspec;
g_assert (n_construct_properties == 1);
pspec = construct_properties->pspec;
/* Check we got the param spec we expected
*/
g_assert (G_IS_PARAM_SPEC_OVERRIDE (pspec));
g_assert (pspec->param_id == BASE_PROP1);
g_assert (strcmp (g_param_spec_get_name (pspec), "prop1") == 0);
g_assert (g_param_spec_get_redirect_target (pspec) == iface_spec1);
/* Test redirection of the nick and blurb to the redirect target
*/
g_assert (strcmp (g_param_spec_get_nick (pspec), "Prop1") == 0);
g_assert (strcmp (g_param_spec_get_blurb (pspec), "Property 1") == 0);
/* Test forwarding of the various GParamSpec methods to the redirect target
*/
g_value_init (&value1, G_TYPE_INT);
g_value_init (&value2, G_TYPE_INT);
g_param_value_set_default (pspec, &value1);
g_assert (g_value_get_int (&value1) == 42);
g_value_reset (&value1);
g_value_set_int (&value1, 0x10000);
g_assert (g_param_value_validate (pspec, &value1));
g_assert (g_value_get_int (&value1) == 0xFFFF);
g_assert (!g_param_value_validate (pspec, &value1));
g_value_reset (&value1);
g_value_set_int (&value1, 1);
g_value_set_int (&value2, 2);
g_assert (g_param_values_cmp (pspec, &value1, &value2) < 0);
g_assert (g_param_values_cmp (pspec, &value2, &value1) > 0);
g_value_unset (&value1);
g_value_unset (&value2);
return base_parent_class->constructor (type,
n_construct_properties,
construct_properties);
}
static void
base_object_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case BASE_PROP1:
g_assert (pspec == inherited_spec1);
base_object->val1 = g_value_get_int (value);
break;
case BASE_PROP2:
g_assert (pspec == inherited_spec2);
base_object->val2 = g_value_get_int (value);
break;
case BASE_PROP3:
g_assert_not_reached ();
break;
case BASE_PROP4:
g_assert_not_reached ();
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base_object_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case BASE_PROP1:
g_assert (pspec == inherited_spec1);
g_value_set_int (value, base_object->val1);
break;
case BASE_PROP2:
g_assert (pspec == inherited_spec2);
g_value_set_int (value, base_object->val2);
break;
case BASE_PROP3:
g_assert_not_reached ();
break;
case BASE_PROP4:
g_assert_not_reached ();
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base_object_notify (GObject *object,
GParamSpec *pspec)
{
/* The property passed to notify is the redirect target, not the
* GParamSpecOverride
*/
g_assert (pspec == inherited_spec1 ||
pspec == inherited_spec2 ||
pspec == inherited_spec3 ||
pspec == inherited_spec4);
}
static void
base_object_class_init (BaseObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
base_parent_class= g_type_class_peek_parent (class);
object_class->constructor = base_object_constructor;
object_class->set_property = base_object_set_property;
object_class->get_property = base_object_get_property;
object_class->notify = base_object_notify;
g_object_class_override_property (object_class, BASE_PROP1, "prop1");
/* We override this one using a real property, not GParamSpecOverride
* We change the flags from READONLY to READWRITE to show that we
* can make the flags less restrictive
*/
inherited_spec2 = g_param_spec_int ("prop2",
"Prop2",
"Property 2",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_READWRITE);
g_object_class_install_property (object_class, BASE_PROP2, inherited_spec2);
g_object_class_override_property (object_class, BASE_PROP3, "prop3");
inherited_spec4 = g_param_spec_int ("prop4",
"Prop4",
"Property 4",
G_MININT, /* min */
G_MAXINT, /* max */
0, /* default */
G_PARAM_READWRITE);
g_object_class_install_property (object_class, BASE_PROP4, inherited_spec4);
}
static void
base_object_init (BaseObject *base_object)
{
base_object->val1 = 42;
}
static DEFINE_TYPE_FULL (BaseObject, base_object,
base_object_class_init, NULL, base_object_init,
G_TYPE_OBJECT,
INTERFACE (NULL, TEST_TYPE_IFACE))
static void
derived_object_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case DERIVED_PROP3:
g_assert (pspec == inherited_spec3);
base_object->val3 = g_value_get_int (value);
break;
case DERIVED_PROP4:
g_assert (pspec == inherited_spec4);
base_object->val4 = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
derived_object_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BaseObject *base_object = BASE_OBJECT (object);
switch (prop_id)
{
case DERIVED_PROP3:
g_assert (pspec == inherited_spec3);
g_value_set_int (value, base_object->val3);
break;
case DERIVED_PROP4:
g_assert (pspec == inherited_spec4);
g_value_set_int (value, base_object->val4);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
derived_object_class_init (DerivedObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = derived_object_set_property;
object_class->get_property = derived_object_get_property;
/* Overriding a property that is itself overriding an interface property */
g_object_class_override_property (object_class, DERIVED_PROP3, "prop3");
/* Overriding a property not from an interface */
g_object_class_override_property (object_class, DERIVED_PROP4, "prop4");
}
static DEFINE_TYPE (DerivedObject, derived_object,
derived_object_class_init, NULL, NULL,
BASE_TYPE_OBJECT)
/* Helper function for testing ...list_properties() */
static void
assert_in_properties (GParamSpec *param_spec,
GParamSpec **properties,
gint n_properties)
{
gint i;
gboolean found = FALSE;
for (i = 0; i < n_properties; i++)
{
if (properties[i] == param_spec)
found = TRUE;
}
g_assert (found);
}
/* Test setting and getting the properties */
static void
test_set (void)
{
BaseObject *object;
gint val1, val2, val3, val4;
object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
g_object_set (object,
"prop1", 0x0101,
"prop2", 0x0202,
"prop3", 0x0303,
"prop4", 0x0404,
NULL);
g_object_get (object,
"prop1", &val1,
"prop2", &val2,
"prop3", &val3,
"prop4", &val4,
NULL);
g_assert (val1 == 0x0101);
g_assert (val2 == 0x0202);
g_assert (val3 == 0x0303);
g_assert (val4 == 0x0404);
g_object_unref (object);
}
/* Test that the right spec is passed on explicit notifications */
static void
test_notify (void)
{
BaseObject *object;
object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
g_object_freeze_notify (G_OBJECT (object));
g_object_notify (G_OBJECT (object), "prop1");
g_object_notify (G_OBJECT (object), "prop2");
g_object_notify (G_OBJECT (object), "prop3");
g_object_notify (G_OBJECT (object), "prop4");
g_object_thaw_notify (G_OBJECT (object));
g_object_unref (object);
}
/* Test g_object_class_find_property() for overridden properties */
static void
test_find_overridden (void)
{
GObjectClass *object_class;
object_class = g_type_class_peek (DERIVED_TYPE_OBJECT);
g_assert (g_object_class_find_property (object_class, "prop1") == inherited_spec1);
g_assert (g_object_class_find_property (object_class, "prop2") == inherited_spec2);
g_assert (g_object_class_find_property (object_class, "prop3") == inherited_spec3);
g_assert (g_object_class_find_property (object_class, "prop4") == inherited_spec4);
}
/* Test g_object_class_list_properties() for overridden properties */
static void
test_list_overridden (void)
{
GObjectClass *object_class;
GParamSpec **properties;
guint n_properties;
object_class = g_type_class_peek (DERIVED_TYPE_OBJECT);
properties = g_object_class_list_properties (object_class, &n_properties);
g_assert (n_properties == 4);
assert_in_properties (inherited_spec1, properties, n_properties);
assert_in_properties (inherited_spec2, properties, n_properties);
assert_in_properties (inherited_spec3, properties, n_properties);
assert_in_properties (inherited_spec4, properties, n_properties);
g_free (properties);
}
/* Test g_object_interface_find_property() */
static void
test_find_interface (void)
{
TestIfaceClass *iface;
iface = g_type_default_interface_peek (TEST_TYPE_IFACE);
g_assert (g_object_interface_find_property (iface, "prop1") == iface_spec1);
g_assert (g_object_interface_find_property (iface, "prop2") == iface_spec2);
g_assert (g_object_interface_find_property (iface, "prop3") == iface_spec3);
}
/* Test g_object_interface_list_properties() */
static void
test_list_interface (void)
{
TestIfaceClass *iface;
GParamSpec **properties;
guint n_properties;
iface = g_type_default_interface_peek (TEST_TYPE_IFACE);
properties = g_object_interface_list_properties (iface, &n_properties);
g_assert (n_properties == 3);
assert_in_properties (iface_spec1, properties, n_properties);
assert_in_properties (iface_spec2, properties, n_properties);
assert_in_properties (iface_spec3, properties, n_properties);
g_free (properties);
}
/* Base2Object, which implements the interface but fails
* to override some of its properties
*/
#define BASE2_TYPE_OBJECT (base2_object_get_type ())
#define BASE2_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE2_TYPE_OBJECT, Base2Object))
typedef struct _Base2Object Base2Object;
typedef struct _Base2ObjectClass Base2ObjectClass;
static void
base2_object_test_iface_init (TestIfaceClass *iface)
{
}
enum {
BASE2_PROP_0,
BASE2_PROP1,
BASE2_PROP2
};
struct _Base2Object
{
GObject parent_instance;
};
struct _Base2ObjectClass
{
GObjectClass parent_class;
};
static GType base2_object_get_type (void);
G_DEFINE_TYPE_WITH_CODE (Base2Object, base2_object, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE,
base2_object_test_iface_init))
static void
base2_object_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case BASE2_PROP1:
g_value_set_int (value, 0);
break;
case BASE2_PROP2:
g_value_set_int (value, 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base2_object_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case BASE2_PROP1:
break;
case BASE2_PROP2:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
base2_object_class_init (Base2ObjectClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->set_property = base2_object_set_property;
object_class->get_property = base2_object_get_property;
g_object_class_override_property (object_class, BASE2_PROP1, "prop1");
g_object_class_override_property (object_class, BASE2_PROP2, "prop2");
}
static void
base2_object_init (Base2Object *object)
{
}
static void
test_not_overridden (void)
{
Base2Object *object;
if (!g_test_undefined ())
return;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=637738");
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Base2Object doesn't implement property 'prop3' from interface 'TestIface'*");
object = g_object_new (BASE2_TYPE_OBJECT, NULL);
g_test_assert_expected_messages ();
g_object_unref (object);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/interface/properties/set", test_set);
g_test_add_func ("/interface/properties/notify", test_notify);
g_test_add_func ("/interface/properties/find-overridden", test_find_overridden);
g_test_add_func ("/interface/properties/list-overridden", test_list_overridden);
g_test_add_func ("/interface/properties/find-interface", test_find_interface);
g_test_add_func ("/interface/properties/list-interface", test_list_interface);
g_test_add_func ("/interface/properties/not-overridden", test_not_overridden);
return g_test_run ();
}

View file

@ -0,0 +1,3 @@
VOID:INT,BOOLEAN,CHAR,UCHAR,UINT,LONG,ULONG,ENUM,FLAGS,FLOAT,DOUBLE,STRING,PARAM,BOXED,POINTER,OBJECT,VARIANT,INT64,UINT64
INT:VOID
UINT:VOID

161
gobject/tests/meson.build Normal file
View file

@ -0,0 +1,161 @@
marshalers_h = custom_target('marshalers_h',
output : 'marshalers.h',
input : 'marshalers.list',
command : [
python, glib_genmarshal,
'--prefix=test',
'--valist-marshallers',
'--output=@OUTPUT@',
'--quiet',
'--header',
'@INPUT@',
],
)
marshalers_c = custom_target('marshalers_c',
output : 'marshalers.c',
input : 'marshalers.list',
command : [
python, glib_genmarshal,
'--prefix=test',
'--valist-marshallers',
'--include-header=marshalers.h',
'--output=@OUTPUT@',
'--quiet',
'--body',
'@INPUT@',
],
)
gobject_tests = {
'qdata' : {},
'boxed' : {},
'enums' : {},
'param' : {},
'threadtests' : {},
'dynamictests' : {},
'binding' : {},
'bindinggroup' : {},
'properties' : {},
'reference' : {},
'flags' : {},
'value' : {},
'type' : {},
'gobject-private' : {
'source' : 'private.c',
},
'closure' : {},
'closure-refcount' : { 'suite': ['slow'] },
'object' : {},
'signal-handler' : {},
'ifaceproperties' : {},
'signals' : {
'source' : ['signals.c', marshalers_h, marshalers_c],
},
'signalgroup' : {},
'testing' : {},
'type-flags' : {},
}
if have_cxx
gobject_tests += {
'cxx' : {
'source' : ['cxx.cpp'],
},
}
endif
if cc.get_id() != 'msvc'
gobject_tests += {'autoptr' : {}}
endif
python_tests = [
'genmarshal.py',
'mkenums.py',
]
# FIXME: put common bits of test environment() in one location
# Not entirely random of course, but at least it changes over time
random_number = minor_version + meson.version().split('.').get(1).to_int()
test_env = environment()
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
test_env.set('G_DEBUG', 'gc-friendly')
test_env.set('MALLOC_CHECK_', '2')
test_env.set('MALLOC_PERTURB_', '@0@'.format(random_number % 256))
test_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
test_cargs = ['-DG_LOG_DOMAIN="GLib-GObject"', '-UG_DISABLE_ASSERT']
foreach test_name, extra_args : gobject_tests
source = extra_args.get('source', test_name + '.c')
install = installed_tests_enabled and extra_args.get('install', true)
if install
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: installed_tests_template_tap,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf
)
endif
exe = executable(test_name, source,
c_args : test_cargs + extra_args.get('c_args', []),
dependencies : test_deps + extra_args.get('dependencies', []),
install_dir: installed_tests_execdir,
install: install,
)
suite = ['gobject'] + extra_args.get('suite', [])
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
# FIXME: https://gitlab.gnome.org/GNOME/glib/issues/1316
# aka https://bugs.debian.org/880883
if test_name == 'closure-refcount' and ['arm', 'aarch64'].contains(host_machine.cpu_family())
timeout = timeout * 10
endif
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
endforeach
foreach test_name : python_tests
test(
test_name,
python,
args: ['-B', files(test_name)],
env: test_env,
suite: ['gobject', 'no-valgrind'],
)
if installed_tests_enabled
install_data(
files(test_name),
install_dir: installed_tests_execdir,
install_mode: 'rwxr-xr-x',
)
test_conf = configuration_data()
test_conf.set('installed_tests_dir', installed_tests_execdir)
test_conf.set('program', test_name)
test_conf.set('env', '')
configure_file(
input: installed_tests_template_tap,
output: test_name + '.test',
install_dir: installed_tests_metadir,
configuration: test_conf,
)
endif
endforeach
# TAP test runner for Python tests
if installed_tests_enabled
install_data(
files('taptestrunner.py'),
install_dir: installed_tests_execdir,
)
endif

739
gobject/tests/mkenums.py Normal file
View file

@ -0,0 +1,739 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Copyright © 2018 Endless Mobile, Inc.
#
# 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.1 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
"""Integration tests for glib-mkenums utility."""
import collections
import os
import shutil
import subprocess
import sys
import tempfile
import textwrap
import unittest
import taptestrunner
Result = collections.namedtuple("Result", ("info", "out", "err", "subs"))
class TestMkenums(unittest.TestCase):
"""Integration test for running glib-mkenums.
This can be run when installed or uninstalled. When uninstalled, it
requires G_TEST_BUILDDIR and G_TEST_SRCDIR to be set.
The idea with this test harness is to test the glib-mkenums utility, its
handling of command line arguments, its exit statuses, and its handling of
various C source codes. In future we could split the core glib-mkenums
parsing and generation code out into a library and unit test that, and
convert this test to just check command line behaviour.
"""
# Track the cwd, we want to back out to that to clean up our tempdir
cwd = ""
rspfile = False
def setUp(self):
self.timeout_seconds = 10 # seconds per test
self.tmpdir = tempfile.TemporaryDirectory()
self.cwd = os.getcwd()
os.chdir(self.tmpdir.name)
print("tmpdir:", self.tmpdir.name)
if "G_TEST_BUILDDIR" in os.environ:
self.__mkenums = os.path.join(
os.environ["G_TEST_BUILDDIR"], "..", "glib-mkenums"
)
else:
self.__mkenums = shutil.which("glib-mkenums")
print("rspfile: {}, mkenums:".format(self.rspfile), self.__mkenums)
def tearDown(self):
os.chdir(self.cwd)
self.tmpdir.cleanup()
def _write_rspfile(self, argv):
import shlex
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, mode="w", delete=False
) as f:
contents = " ".join([shlex.quote(arg) for arg in argv])
print("Response file contains:", contents)
f.write(contents)
f.flush()
return f.name
def runMkenums(self, *args):
if self.rspfile:
rspfile = self._write_rspfile(args)
args = ["@" + rspfile]
argv = [self.__mkenums]
# shebang lines are not supported on native
# Windows consoles
if os.name == "nt":
argv.insert(0, sys.executable)
argv.extend(args)
print("Running:", argv)
env = os.environ.copy()
env["LC_ALL"] = "C.UTF-8"
print("Environment:", env)
# We want to ensure consistent line endings...
info = subprocess.run(
argv,
timeout=self.timeout_seconds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
universal_newlines=True,
)
info.check_returncode()
out = info.stdout.strip()
err = info.stderr.strip()
# Known substitutions for standard boilerplate
subs = {
"standard_top_comment": "This file is generated by glib-mkenums, do not modify "
"it. This code is licensed under the same license as the "
"containing project. Note that it links to GLib, so must "
"comply with the LGPL linking clauses.",
"standard_bottom_comment": "Generated data ends here",
}
result = Result(info, out, err, subs)
print("Output:", result.out)
return result
def runMkenumsWithTemplate(self, template_contents, *args):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".template", delete=False
) as template_file:
# Write out the template.
template_file.write(template_contents.encode("utf-8"))
print(template_file.name + ":", template_contents)
template_file.flush()
return self.runMkenums("--template", template_file.name, *args)
def runMkenumsWithAllSubstitutions(self, *args):
"""Run glib-mkenums with a template which outputs all substitutions."""
template_contents = """
/*** BEGIN file-header ***/
file-header
/*** END file-header ***/
/*** BEGIN file-production ***/
file-production
filename: @filename@
basename: @basename@
/*** END file-production ***/
/*** BEGIN enumeration-production ***/
enumeration-production
EnumName: @EnumName@
enum_name: @enum_name@
ENUMNAME: @ENUMNAME@
ENUMSHORT: @ENUMSHORT@
ENUMPREFIX: @ENUMPREFIX@
enumsince: @enumsince@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END enumeration-production ***/
/*** BEGIN value-header ***/
value-header
EnumName: @EnumName@
enum_name: @enum_name@
ENUMNAME: @ENUMNAME@
ENUMSHORT: @ENUMSHORT@
ENUMPREFIX: @ENUMPREFIX@
enumsince: @enumsince@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END value-header ***/
/*** BEGIN value-production ***/
value-production
VALUENAME: @VALUENAME@
valuenick: @valuenick@
valuenum: @valuenum@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END value-production ***/
/*** BEGIN value-tail ***/
value-tail
EnumName: @EnumName@
enum_name: @enum_name@
ENUMNAME: @ENUMNAME@
ENUMSHORT: @ENUMSHORT@
ENUMPREFIX: @ENUMPREFIX@
enumsince: @enumsince@
type: @type@
Type: @Type@
TYPE: @TYPE@
/*** END value-tail ***/
/*** BEGIN comment ***/
comment
comment: @comment@
/*** END comment ***/
/*** BEGIN file-tail ***/
file-tail
/*** END file-tail ***/
"""
return self.runMkenumsWithTemplate(template_contents, *args)
def runMkenumsWithHeader(self, h_contents, encoding="utf-8"):
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix=".h", delete=False
) as h_file:
# Write out the header to be scanned.
h_file.write(h_contents.encode(encoding))
print(h_file.name + ":", h_contents)
h_file.flush()
# Run glib-mkenums with a template which outputs all substitutions.
result = self.runMkenumsWithAllSubstitutions(h_file.name)
# Known substitutions for generated filenames.
result.subs.update(
{"filename": h_file.name, "basename": os.path.basename(h_file.name)}
)
return result
def assertSingleEnum(
self,
result,
enum_name_camel,
enum_name_lower,
enum_name_upper,
enum_name_short,
enum_prefix,
enum_since,
type_lower,
type_camel,
type_upper,
value_name,
value_nick,
value_num,
):
"""Assert that out (from runMkenumsWithHeader()) contains a single
enum and value matching the given arguments."""
subs = dict(
{
"enum_name_camel": enum_name_camel,
"enum_name_lower": enum_name_lower,
"enum_name_upper": enum_name_upper,
"enum_name_short": enum_name_short,
"enum_prefix": enum_prefix,
"enum_since": enum_since,
"type_lower": type_lower,
"type_camel": type_camel,
"type_upper": type_upper,
"value_name": value_name,
"value_nick": value_nick,
"value_num": value_num,
},
**result.subs
)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
file-production
filename: {filename}
basename: {basename}
enumeration-production
EnumName: {enum_name_camel}
enum_name: {enum_name_lower}
ENUMNAME: {enum_name_upper}
ENUMSHORT: {enum_name_short}
ENUMPREFIX: {enum_prefix}
enumsince: {enum_since}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
value-header
EnumName: {enum_name_camel}
enum_name: {enum_name_lower}
ENUMNAME: {enum_name_upper}
ENUMSHORT: {enum_name_short}
ENUMPREFIX: {enum_prefix}
enumsince: {enum_since}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
value-production
VALUENAME: {value_name}
valuenick: {value_nick}
valuenum: {value_num}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
value-tail
EnumName: {enum_name_camel}
enum_name: {enum_name_lower}
ENUMNAME: {enum_name_upper}
ENUMSHORT: {enum_name_short}
ENUMPREFIX: {enum_prefix}
enumsince: {enum_since}
type: {type_lower}
Type: {type_camel}
TYPE: {type_upper}
file-tail
comment
comment: {standard_bottom_comment}
""".format(
**subs
).strip(),
result.out,
)
def test_help(self):
"""Test the --help argument."""
result = self.runMkenums("--help")
self.assertIn("usage: glib-mkenums", result.out)
def test_no_args(self):
"""Test running with no arguments at all."""
result = self.runMkenums()
self.assertEqual("", result.err)
self.assertEqual(
"""/* {standard_top_comment} */
/* {standard_bottom_comment} */""".format(
**result.subs
),
result.out.strip(),
)
def test_empty_template(self):
"""Test running with an empty template and no header files."""
result = self.runMkenumsWithTemplate("")
self.assertEqual("", result.err)
self.assertEqual(
"""/* {standard_top_comment} */
/* {standard_bottom_comment} */""".format(
**result.subs
),
result.out.strip(),
)
def test_no_headers(self):
"""Test running with a complete template, but no header files."""
result = self.runMkenumsWithAllSubstitutions()
self.assertEqual("", result.err)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
file-tail
comment
comment: {standard_bottom_comment}
""".format(
**result.subs
).strip(),
result.out,
)
def test_empty_header(self):
"""Test an empty header."""
result = self.runMkenumsWithHeader("")
self.assertEqual("", result.err)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
file-tail
comment
comment: {standard_bottom_comment}
""".format(
**result.subs
).strip(),
result.out,
)
def test_enum_name(self):
"""Test typedefs with an enum and a typedef name. Bug #794506."""
h_contents = """
typedef enum _SomeEnumIdentifier {
ENUM_VALUE
} SomeEnumIdentifier;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumIdentifier",
"some_enum_identifier",
"SOME_ENUM_IDENTIFIER",
"ENUM_IDENTIFIER",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE",
"value",
"0",
)
def test_non_utf8_encoding(self):
"""Test source files with non-UTF-8 encoding. Bug #785113."""
h_contents = """
/* Copyright © La Peña */
typedef enum {
ENUM_VALUE
} SomeEnumIdentifier;
"""
result = self.runMkenumsWithHeader(h_contents, encoding="iso-8859-1")
self.assertIn("WARNING: UnicodeWarning: ", result.err)
self.assertSingleEnum(
result,
"SomeEnumIdentifier",
"some_enum_identifier",
"SOME_ENUM_IDENTIFIER",
"ENUM_IDENTIFIER",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE",
"value",
"0",
)
def test_reproducible(self):
"""Test builds are reproducible regardless of file ordering.
Bug #691436."""
template_contents = "template"
h_contents1 = """
typedef enum {
FIRST,
} Header1;
"""
h_contents2 = """
typedef enum {
SECOND,
} Header2;
"""
with tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="1.h", delete=False
) as h_file1, tempfile.NamedTemporaryFile(
dir=self.tmpdir.name, suffix="2.h", delete=False
) as h_file2:
# Write out the headers.
h_file1.write(h_contents1.encode("utf-8"))
h_file2.write(h_contents2.encode("utf-8"))
h_file1.flush()
h_file2.flush()
# Run glib-mkenums with the headers in one order, and then again
# in another order.
result1 = self.runMkenumsWithTemplate(
template_contents, h_file1.name, h_file2.name
)
self.assertEqual("", result1.err)
result2 = self.runMkenumsWithTemplate(
template_contents, h_file2.name, h_file1.name
)
self.assertEqual("", result2.err)
# The output should be the same.
self.assertEqual(result1.out, result2.out)
def test_no_nick(self):
"""Test trigraphs with a desc but no nick. Issue #1360."""
h_contents = """
typedef enum {
GEGL_SAMPLER_NEAREST = 0, /*< desc="nearest" >*/
} GeglSamplerType;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GeglSamplerType",
"gegl_sampler_type",
"GEGL_SAMPLER_TYPE",
"SAMPLER_TYPE",
"GEGL",
"",
"enum",
"Enum",
"ENUM",
"GEGL_SAMPLER_NEAREST",
"nearest",
"0",
)
def test_filename_basename_in_fhead_ftail(self):
template_contents = """
/*** BEGIN file-header ***/
file-header
filename: @filename@
basename: @basename@
/*** END file-header ***/
/*** BEGIN comment ***/
comment
comment: @comment@
/*** END comment ***/
/*** BEGIN file-tail ***/
file-tail
filename: @filename@
basename: @basename@
/*** END file-tail ***/"""
result = self.runMkenumsWithTemplate(template_contents)
self.assertEqual(
textwrap.dedent(
"""
WARNING: @filename@ used in file-header section.
WARNING: @basename@ used in file-header section.
WARNING: @filename@ used in file-tail section.
WARNING: @basename@ used in file-tail section.
"""
).strip(),
result.err,
)
self.assertEqual(
"""
comment
comment: {standard_top_comment}
file-header
filename: @filename@
basename: @basename@
file-tail
filename: @filename@
basename: @basename@
comment
comment: {standard_bottom_comment}
""".format(
**result.subs
).strip(),
result.out,
)
def test_since(self):
"""Test user-provided 'since' version handling
https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1492"""
h_contents = """
typedef enum { /*< since=1.0 >*/
QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0,
} QmiWmsMessageProtocol;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"QmiWmsMessageProtocol",
"qmi_wms_message_protocol",
"QMI_WMS_MESSAGE_PROTOCOL",
"WMS_MESSAGE_PROTOCOL",
"QMI",
"1.0",
"enum",
"Enum",
"ENUM",
"QMI_WMS_MESSAGE_PROTOCOL_CDMA",
"cdma",
"0",
)
def test_enum_private_public(self):
"""Test private/public enums. Bug #782162."""
h_contents1 = """
typedef enum {
ENUM_VALUE_PUBLIC1,
/*< private >*/
ENUM_VALUE_PRIVATE,
} SomeEnumA
"""
h_contents2 = """
typedef enum {
/*< private >*/
ENUM_VALUE_PRIVATE,
/*< public >*/
ENUM_VALUE_PUBLIC2,
} SomeEnumB;
"""
result = self.runMkenumsWithHeader(h_contents1)
self.maxDiff = None
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumA",
"some_enum_a",
"SOME_ENUM_A",
"ENUM_A",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE_PUBLIC1",
"public1",
"0",
)
result = self.runMkenumsWithHeader(h_contents2)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"SomeEnumB",
"some_enum_b",
"SOME_ENUM_B",
"ENUM_B",
"SOME",
"",
"enum",
"Enum",
"ENUM",
"ENUM_VALUE_PUBLIC2",
"public2",
"0",
)
def test_available_in(self):
"""Test GLIB_AVAILABLE_ENUMERATOR_IN_2_68 handling
https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
h_contents = """
typedef enum {
G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_AVAILABLE_ENUMERATOR_IN_2_68 = (1<<2)
} GDBusServerFlags;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GDBusServerFlags",
"g_dbus_server_flags",
"G_DBUS_SERVER_FLAGS",
"DBUS_SERVER_FLAGS",
"G",
"",
"flags",
"Flags",
"FLAGS",
"G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
"user",
"4",
)
def test_deprecated_in(self):
"""Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68 handling
https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
h_contents = """
typedef enum {
G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68 = (1<<2)
} GDBusServerFlags;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GDBusServerFlags",
"g_dbus_server_flags",
"G_DBUS_SERVER_FLAGS",
"DBUS_SERVER_FLAGS",
"G",
"",
"flags",
"Flags",
"FLAGS",
"G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
"user",
"4",
)
def test_deprecated_in_for(self):
"""Test GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR() handling
https://gitlab.gnome.org/GNOME/glib/-/issues/2327"""
h_contents = """
typedef enum {
G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR(G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER2) = (1<<2)
} GDBusServerFlags;
"""
result = self.runMkenumsWithHeader(h_contents)
self.assertEqual("", result.err)
self.assertSingleEnum(
result,
"GDBusServerFlags",
"g_dbus_server_flags",
"G_DBUS_SERVER_FLAGS",
"DBUS_SERVER_FLAGS",
"G",
"",
"flags",
"Flags",
"FLAGS",
"G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER",
"user",
"4",
)
class TestRspMkenums(TestMkenums):
"""Run all tests again in @rspfile mode"""
rspfile = True
if __name__ == "__main__":
unittest.main(testRunner=taptestrunner.TAPTestRunner())

150
gobject/tests/object.c Normal file
View file

@ -0,0 +1,150 @@
#include <glib-object.h>
/* --------------------------------- */
/* test_object_constructor_singleton */
typedef GObject MySingletonObject;
typedef GObjectClass MySingletonObjectClass;
GType my_singleton_object_get_type (void);
G_DEFINE_TYPE (MySingletonObject, my_singleton_object, G_TYPE_OBJECT)
static MySingletonObject *singleton;
static void
my_singleton_object_init (MySingletonObject *obj)
{
}
static GObject *
my_singleton_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
if (singleton)
return g_object_ref (singleton);
object = G_OBJECT_CLASS (my_singleton_object_parent_class)->
constructor (type, n_construct_properties, construct_params);
singleton = (MySingletonObject *)object;
return object;
}
static void
my_singleton_object_finalize (MySingletonObject *obj)
{
singleton = NULL;
G_OBJECT_CLASS (my_singleton_object_parent_class)->finalize (obj);
}
static void
my_singleton_object_class_init (MySingletonObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = my_singleton_object_constructor;
object_class->finalize = my_singleton_object_finalize;
}
static void
test_object_constructor_singleton (void)
{
MySingletonObject *one, *two, *three;
one = g_object_new (my_singleton_object_get_type (), NULL);
g_assert_cmpint (G_OBJECT (one)->ref_count, ==, 1);
two = g_object_new (my_singleton_object_get_type (), NULL);
g_assert (one == two);
g_assert_cmpint (G_OBJECT (two)->ref_count, ==, 2);
three = g_object_new (my_singleton_object_get_type (), NULL);
g_assert (one == three);
g_assert_cmpint (G_OBJECT (three)->ref_count, ==, 3);
g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
g_object_unref (one);
g_assert (one != NULL);
g_object_unref (three);
g_object_unref (two);
g_assert (one == NULL);
}
/* ----------------------------------- */
/* test_object_constructor_infanticide */
typedef GObject MyInfanticideObject;
typedef GObjectClass MyInfanticideObjectClass;
GType my_infanticide_object_get_type (void);
G_DEFINE_TYPE (MyInfanticideObject, my_infanticide_object, G_TYPE_OBJECT)
static void
my_infanticide_object_init (MyInfanticideObject *obj)
{
}
static GObject *
my_infanticide_object_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_params)
{
GObject *object;
object = G_OBJECT_CLASS (my_infanticide_object_parent_class)->
constructor (type, n_construct_properties, construct_params);
g_object_unref (object);
return NULL;
}
static void
my_infanticide_object_class_init (MyInfanticideObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructor = my_infanticide_object_constructor;
}
static void
test_object_constructor_infanticide (void)
{
GObject *obj;
int i;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=661576");
for (i = 0; i < 1000; i++)
{
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
"*finalized while still in-construction*");
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
"*Custom constructor*returned NULL*");
obj = g_object_new (my_infanticide_object_get_type (), NULL);
g_assert_null (obj);
g_test_assert_expected_messages ();
}
}
/* --------------------------------- */
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/object/constructor/singleton", test_object_constructor_singleton);
g_test_add_func ("/object/constructor/infanticide", test_object_constructor_infanticide);
return g_test_run ();
}

1266
gobject/tests/param.c Normal file

File diff suppressed because it is too large Load diff

261
gobject/tests/private.c Normal file
View file

@ -0,0 +1,261 @@
/* We are testing some deprecated APIs here */
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include <glib-object.h>
typedef struct {
GObject parent_instance;
} TestObject;
typedef struct {
int dummy_0;
float dummy_1;
} TestObjectPrivate;
typedef struct {
GObjectClass parent_class;
} TestObjectClass;
GType test_object_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestObject, test_object, G_TYPE_OBJECT,
G_ADD_PRIVATE (TestObject))
static void
test_object_class_init (TestObjectClass *klass)
{
}
static void
test_object_init (TestObject *self)
{
TestObjectPrivate *priv = test_object_get_instance_private (self);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestObject_private_offset);
priv->dummy_0 = 42;
priv->dummy_1 = 3.14159f;
}
static int
test_object_get_dummy_0 (TestObject *self)
{
TestObjectPrivate *priv = test_object_get_instance_private (self);
return priv->dummy_0;
}
static float
test_object_get_dummy_1 (TestObject *self)
{
TestObjectPrivate *priv = test_object_get_instance_private (self);
return priv->dummy_1;
}
typedef struct {
TestObject parent_instance;
} TestDerived;
typedef struct {
char *dummy_2;
} TestDerivedPrivate;
typedef struct {
TestObjectClass parent_class;
} TestDerivedClass;
GType test_derived_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestDerived, test_derived, test_object_get_type (),
G_ADD_PRIVATE (TestDerived))
static void
test_derived_finalize (GObject *obj)
{
TestDerivedPrivate *priv = test_derived_get_instance_private ((TestDerived *) obj);
g_free (priv->dummy_2);
G_OBJECT_CLASS (test_derived_parent_class)->finalize (obj);
}
static void
test_derived_class_init (TestDerivedClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = test_derived_finalize;
}
static void
test_derived_init (TestDerived *self)
{
TestDerivedPrivate *priv = test_derived_get_instance_private (self);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestDerived_private_offset);
priv->dummy_2 = g_strdup ("Hello");
}
static const char *
test_derived_get_dummy_2 (TestDerived *self)
{
TestDerivedPrivate *priv = test_derived_get_instance_private (self);
return priv->dummy_2;
}
typedef struct {
TestObject parent_instance;
} TestMixed;
typedef struct {
gint dummy_3;
} TestMixedPrivate;
typedef struct {
TestObjectClass parent_class;
} TestMixedClass;
GType test_mixed_get_type (void);
G_DEFINE_TYPE (TestMixed, test_mixed, test_object_get_type ())
static void
test_mixed_class_init (TestMixedClass *klass)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_type_class_add_private (klass, sizeof (TestMixedPrivate));
G_GNUC_END_IGNORE_DEPRECATIONS
}
static void
test_mixed_init (TestMixed *self)
{
TestMixedPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, test_mixed_get_type (), TestMixedPrivate);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestMixed_private_offset);
priv->dummy_3 = 47;
}
static gint
test_mixed_get_dummy_3 (TestMixed *self)
{
TestMixedPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, test_mixed_get_type (), TestMixedPrivate);
return priv->dummy_3;
}
typedef struct {
TestMixed parent_instance;
} TestMixedDerived;
typedef struct {
gint64 dummy_4;
} TestMixedDerivedPrivate;
typedef struct {
TestMixedClass parent_class;
} TestMixedDerivedClass;
GType test_mixed_derived_get_type (void);
G_DEFINE_TYPE_WITH_CODE (TestMixedDerived, test_mixed_derived, test_mixed_get_type (),
G_ADD_PRIVATE (TestMixedDerived))
static void
test_mixed_derived_class_init (TestMixedDerivedClass *klass)
{
}
static void
test_mixed_derived_init (TestMixedDerived *self)
{
TestMixedDerivedPrivate *priv = test_mixed_derived_get_instance_private (self);
if (g_test_verbose ())
g_printerr ("Offset of %sPrivate for type '%s': %d\n",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (self),
TestMixedDerived_private_offset);
priv->dummy_4 = g_get_monotonic_time ();
}
static gint64
test_mixed_derived_get_dummy_4 (TestMixedDerived *self)
{
TestMixedDerivedPrivate *priv = test_mixed_derived_get_instance_private (self);
return priv->dummy_4;
}
static void
private_instance (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
gpointer class;
gint offset;
g_assert_cmpint (test_object_get_dummy_0 (obj), ==, 42);
g_assert_cmpfloat (test_object_get_dummy_1 (obj), ==, 3.14159f);
class = g_type_class_ref (test_object_get_type ());
offset = g_type_class_get_instance_private_offset (class);
g_type_class_unref (class);
g_assert (offset == TestObject_private_offset);
g_object_unref (obj);
}
static void
private_derived_instance (void)
{
TestDerived *obj = g_object_new (test_derived_get_type (), NULL);
g_assert_cmpstr (test_derived_get_dummy_2 (obj), ==, "Hello");
g_assert_cmpint (test_object_get_dummy_0 ((TestObject *) obj), ==, 42);
g_object_unref (obj);
}
static void
private_mixed_derived_instance (void)
{
TestMixedDerived *derived = g_object_new (test_mixed_derived_get_type (), NULL);
TestMixed *mixed = g_object_new (test_mixed_get_type (), NULL);
g_assert_cmpint (test_mixed_get_dummy_3 (mixed), ==, 47);
g_assert (test_mixed_derived_get_dummy_4 (derived) <= g_get_monotonic_time ());
g_object_unref (derived);
g_object_unref (mixed);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/private/instance", private_instance);
g_test_add_func ("/private/derived-instance", private_derived_instance);
g_test_add_func ("/private/mixed-derived-instance", private_mixed_derived_instance);
return g_test_run ();
}

658
gobject/tests/properties.c Normal file
View file

@ -0,0 +1,658 @@
#include <stdlib.h>
#include <gstdio.h>
#include <glib-object.h>
typedef struct _TestObject {
GObject parent_instance;
gint foo;
gboolean bar;
gchar *baz;
gchar *quux;
} TestObject;
typedef struct _TestObjectClass {
GObjectClass parent_class;
} TestObjectClass;
enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, PROP_QUUX, N_PROPERTIES };
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
static GType test_object_get_type (void);
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
static void
test_object_set_foo (TestObject *obj,
gint foo)
{
if (obj->foo != foo)
{
obj->foo = foo;
g_assert (properties[PROP_FOO] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
}
}
static void
test_object_set_bar (TestObject *obj,
gboolean bar)
{
bar = !!bar;
if (obj->bar != bar)
{
obj->bar = bar;
g_assert (properties[PROP_BAR] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
}
}
static void
test_object_set_baz (TestObject *obj,
const gchar *baz)
{
if (g_strcmp0 (obj->baz, baz) != 0)
{
g_free (obj->baz);
obj->baz = g_strdup (baz);
g_assert (properties[PROP_BAZ] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
}
}
static void
test_object_set_quux (TestObject *obj,
const gchar *quux)
{
if (g_strcmp0 (obj->quux, quux) != 0)
{
g_free (obj->quux);
obj->quux = g_strdup (quux);
g_assert (properties[PROP_QUUX] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_QUUX]);
}
}
static void
test_object_finalize (GObject *gobject)
{
TestObject *self = (TestObject *) gobject;
g_free (self->baz);
g_free (self->quux);
/* When the ref_count of an object is zero it is still
* possible to notify the property, but it should do
* nothing and silently quit (bug #705570)
*/
g_object_notify (gobject, "foo");
g_object_notify_by_pspec (gobject, properties[PROP_BAR]);
G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
}
static void
test_object_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
TestObject *tobj = (TestObject *) gobject;
g_assert_cmpint (prop_id, !=, 0);
g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
switch (prop_id)
{
case PROP_FOO:
test_object_set_foo (tobj, g_value_get_int (value));
break;
case PROP_BAR:
test_object_set_bar (tobj, g_value_get_boolean (value));
break;
case PROP_BAZ:
test_object_set_baz (tobj, g_value_get_string (value));
break;
case PROP_QUUX:
test_object_set_quux (tobj, g_value_get_string (value));
break;
default:
g_assert_not_reached ();
}
}
static void
test_object_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
TestObject *tobj = (TestObject *) gobject;
g_assert_cmpint (prop_id, !=, 0);
g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
switch (prop_id)
{
case PROP_FOO:
g_value_set_int (value, tobj->foo);
break;
case PROP_BAR:
g_value_set_boolean (value, tobj->bar);
break;
case PROP_BAZ:
g_value_set_string (value, tobj->baz);
break;
case PROP_QUUX:
g_value_set_string (value, tobj->quux);
break;
default:
g_assert_not_reached ();
}
}
static void
test_object_class_init (TestObjectClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo",
-1, G_MAXINT,
0,
G_PARAM_READWRITE);
properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
FALSE,
G_PARAM_READWRITE);
properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
NULL,
G_PARAM_READWRITE);
properties[PROP_QUUX] = g_param_spec_string ("quux", "quux", "quux",
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
gobject_class->set_property = test_object_set_property;
gobject_class->get_property = test_object_get_property;
gobject_class->finalize = test_object_finalize;
g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
}
static void
test_object_init (TestObject *self)
{
self->foo = 42;
self->bar = TRUE;
self->baz = g_strdup ("Hello");
self->quux = NULL;
}
static void
properties_install (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
GParamSpec *pspec;
g_assert (properties[PROP_FOO] != NULL);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo");
g_assert (properties[PROP_FOO] == pspec);
g_object_unref (obj);
}
typedef struct {
const gchar *name;
GParamSpec *pspec;
gboolean fired;
} TestNotifyClosure;
static void
on_notify (GObject *gobject,
GParamSpec *pspec,
TestNotifyClosure *closure)
{
g_assert (closure->pspec == pspec);
g_assert_cmpstr (closure->name, ==, pspec->name);
closure->fired = TRUE;
}
static void
properties_notify (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
TestNotifyClosure closure;
g_assert (properties[PROP_FOO] != NULL);
g_assert (properties[PROP_QUUX] != NULL);
g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &closure);
closure.name = "foo";
closure.pspec = properties[PROP_FOO];
closure.fired = FALSE;
g_object_set (obj, "foo", 47, NULL);
g_assert (closure.fired);
closure.name = "baz";
closure.pspec = properties[PROP_BAZ];
closure.fired = FALSE;
g_object_set (obj, "baz", "something new", NULL);
g_assert (closure.fired);
/* baz lacks explicit notify, so we will see this twice */
closure.fired = FALSE;
g_object_set (obj, "baz", "something new", NULL);
g_assert (closure.fired);
/* quux on the other hand, ... */
closure.name = "quux";
closure.pspec = properties[PROP_QUUX];
closure.fired = FALSE;
g_object_set (obj, "quux", "something new", NULL);
g_assert (closure.fired);
/* no change; no notify */
closure.fired = FALSE;
g_object_set (obj, "quux", "something new", NULL);
g_assert (!closure.fired);
g_object_unref (obj);
}
typedef struct {
GParamSpec *pspec[3];
gint pos;
} Notifys;
static void
on_notify2 (GObject *gobject,
GParamSpec *pspec,
Notifys *n)
{
g_assert (n->pspec[n->pos] == pspec);
n->pos++;
}
static void
properties_notify_queue (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
Notifys n;
g_assert (properties[PROP_FOO] != NULL);
n.pspec[0] = properties[PROP_BAZ];
n.pspec[1] = properties[PROP_BAR];
n.pspec[2] = properties[PROP_FOO];
n.pos = 0;
g_signal_connect (obj, "notify", G_CALLBACK (on_notify2), &n);
g_object_freeze_notify (G_OBJECT (obj));
g_object_set (obj, "foo", 47, NULL);
g_object_set (obj, "bar", TRUE, "foo", 42, "baz", "abc", NULL);
g_object_thaw_notify (G_OBJECT (obj));
g_assert (n.pos == 3);
g_object_unref (obj);
}
static void
properties_construct (void)
{
TestObject *obj;
gint val;
gboolean b;
gchar *s;
g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=630357");
/* more than 16 args triggers a realloc in g_object_new_valist() */
obj = g_object_new (test_object_get_type (),
"foo", 1,
"foo", 2,
"foo", 3,
"foo", 4,
"foo", 5,
"bar", FALSE,
"foo", 6,
"foo", 7,
"foo", 8,
"foo", 9,
"foo", 10,
"baz", "boo",
"foo", 11,
"foo", 12,
"foo", 13,
"foo", 14,
"foo", 15,
"foo", 16,
"foo", 17,
"foo", 18,
NULL);
g_object_get (obj, "foo", &val, NULL);
g_assert (val == 18);
g_object_get (obj, "bar", &b, NULL);
g_assert (!b);
g_object_get (obj, "baz", &s, NULL);
g_assert_cmpstr (s, ==, "boo");
g_free (s);
g_object_unref (obj);
}
static void
properties_testv_with_no_properties (void)
{
TestObject *test_obj;
const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
GValue values_out[4] = { G_VALUE_INIT };
guint i;
/* Test newv_with_properties && getv */
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 0, NULL, NULL);
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
/* It should have init values */
g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 42);
g_assert_true (g_value_get_boolean (&values_out[1]));
g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "Hello");
g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, NULL);
for (i = 0; i < 4; i++)
g_value_unset (&values_out[i]);
g_object_unref (test_obj);
}
static void
properties_testv_with_valid_properties (void)
{
TestObject *test_obj;
const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
GValue values_in[4] = { G_VALUE_INIT };
GValue values_out[4] = { G_VALUE_INIT };
guint i;
g_value_init (&(values_in[0]), G_TYPE_INT);
g_value_set_int (&(values_in[0]), 100);
g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
g_value_set_boolean (&(values_in[1]), TRUE);
g_value_init (&(values_in[2]), G_TYPE_STRING);
g_value_set_string (&(values_in[2]), "pigs");
g_value_init (&(values_in[3]), G_TYPE_STRING);
g_value_set_string (&(values_in[3]), "fly");
/* Test newv_with_properties && getv */
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 4, prop_names, values_in);
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 100);
g_assert_true (g_value_get_boolean (&values_out[1]));
g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "pigs");
g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, "fly");
for (i = 0; i < G_N_ELEMENTS (values_out); i++)
g_value_unset (&values_out[i]);
/* Test newv2 && getv */
g_value_set_string (&(values_in[2]), "Elmo knows");
g_value_set_string (&(values_in[3]), "where you live");
g_object_setv (G_OBJECT (test_obj), 4, prop_names, values_in);
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 100);
g_assert_true (g_value_get_boolean (&values_out[1]));
g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "Elmo knows");
g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, "where you live");
for (i = 0; i < G_N_ELEMENTS (values_in); i++)
g_value_unset (&values_in[i]);
for (i = 0; i < G_N_ELEMENTS (values_out); i++)
g_value_unset (&values_out[i]);
g_object_unref (test_obj);
}
static void
properties_testv_with_invalid_property_type (void)
{
if (g_test_subprocess ())
{
TestObject *test_obj;
const char *invalid_prop_names[1] = { "foo" };
GValue values_in[1] = { G_VALUE_INIT };
g_value_init (&(values_in[0]), G_TYPE_STRING);
g_value_set_string (&(values_in[0]), "fly");
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 1, invalid_prop_names, values_in);
/* should give a warning */
g_object_unref (test_obj);
}
g_test_trap_subprocess (NULL, 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*WARNING*foo*gint*gchararray*");
}
static void
properties_testv_with_invalid_property_names (void)
{
if (g_test_subprocess ())
{
TestObject *test_obj;
const char *invalid_prop_names[4] = { "foo", "boo", "moo", "poo" };
GValue values_in[4] = { G_VALUE_INIT };
g_value_init (&(values_in[0]), G_TYPE_INT);
g_value_set_int (&(values_in[0]), 100);
g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
g_value_set_boolean (&(values_in[1]), TRUE);
g_value_init (&(values_in[2]), G_TYPE_STRING);
g_value_set_string (&(values_in[2]), "pigs");
g_value_init (&(values_in[3]), G_TYPE_STRING);
g_value_set_string (&(values_in[3]), "fly");
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 4, invalid_prop_names, values_in);
/* This call should give 3 Critical warnings. Actually, a critical warning
* shouldn't make g_object_new_with_properties to fail when a bad named
* property is given, because, it will just ignore that property. However,
* for test purposes, it is considered that the test doesn't pass.
*/
g_object_unref (test_obj);
}
g_test_trap_subprocess (NULL, 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*CRITICAL*g_object_new_is_valid_property*boo*");
}
static void
properties_testv_getv (void)
{
TestObject *test_obj;
const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
GValue values_out_initialized[4] = { G_VALUE_INIT };
GValue values_out_uninitialized[4] = { G_VALUE_INIT };
guint i;
g_value_init (&(values_out_initialized[0]), G_TYPE_INT);
g_value_init (&(values_out_initialized[1]), G_TYPE_BOOLEAN);
g_value_init (&(values_out_initialized[2]), G_TYPE_STRING);
g_value_init (&(values_out_initialized[3]), G_TYPE_STRING);
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 0, NULL, NULL);
/* Test g_object_getv for an initialized values array */
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out_initialized);
/* It should have init values */
g_assert_cmpint (g_value_get_int (&values_out_initialized[0]), ==, 42);
g_assert_true (g_value_get_boolean (&values_out_initialized[1]));
g_assert_cmpstr (g_value_get_string (&values_out_initialized[2]), ==, "Hello");
g_assert_cmpstr (g_value_get_string (&values_out_initialized[3]), ==, NULL);
/* Test g_object_getv for an uninitialized values array */
g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out_uninitialized);
/* It should have init values */
g_assert_cmpint (g_value_get_int (&values_out_uninitialized[0]), ==, 42);
g_assert_true (g_value_get_boolean (&values_out_uninitialized[1]));
g_assert_cmpstr (g_value_get_string (&values_out_uninitialized[2]), ==, "Hello");
g_assert_cmpstr (g_value_get_string (&values_out_uninitialized[3]), ==, NULL);
for (i = 0; i < 4; i++)
{
g_value_unset (&values_out_initialized[i]);
g_value_unset (&values_out_uninitialized[i]);
}
g_object_unref (test_obj);
}
static void
properties_get_property (void)
{
TestObject *test_obj;
struct {
const char *name;
GType gtype;
GValue value;
} test_props[] = {
{ "foo", G_TYPE_INT, G_VALUE_INIT },
{ "bar", G_TYPE_INVALID, G_VALUE_INIT },
{ "bar", G_TYPE_STRING, G_VALUE_INIT },
};
gsize i;
g_test_summary ("g_object_get_property() accepts uninitialized, "
"initialized, and transformable values");
for (i = 0; i < G_N_ELEMENTS (test_props); i++)
{
if (test_props[i].gtype != G_TYPE_INVALID)
g_value_init (&(test_props[i].value), test_props[i].gtype);
}
test_obj = (TestObject *) g_object_new_with_properties (test_object_get_type (), 0, NULL, NULL);
g_test_message ("Test g_object_get_property with an initialized value");
g_object_get_property (G_OBJECT (test_obj), test_props[0].name, &(test_props[0].value));
g_assert_cmpint (g_value_get_int (&(test_props[0].value)), ==, 42);
g_test_message ("Test g_object_get_property with an uninitialized value");
g_object_get_property (G_OBJECT (test_obj), test_props[1].name, &(test_props[1].value));
g_assert_true (g_value_get_boolean (&(test_props[1].value)));
g_test_message ("Test g_object_get_property with a transformable value");
g_object_get_property (G_OBJECT (test_obj), test_props[2].name, &(test_props[2].value));
g_assert_true (G_VALUE_HOLDS_STRING (&(test_props[2].value)));
g_assert_cmpstr (g_value_get_string (&(test_props[2].value)), ==, "TRUE");
for (i = 0; i < G_N_ELEMENTS (test_props); i++)
g_value_unset (&(test_props[i].value));
g_object_unref (test_obj);
}
static void
properties_testv_notify_queue (void)
{
TestObject *test_obj;
const char *prop_names[3] = { "foo", "bar", "baz" };
GValue values_in[3] = { G_VALUE_INIT };
Notifys n;
guint i;
g_value_init (&(values_in[0]), G_TYPE_INT);
g_value_set_int (&(values_in[0]), 100);
g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
g_value_set_boolean (&(values_in[1]), TRUE);
g_value_init (&(values_in[2]), G_TYPE_STRING);
g_value_set_string (&(values_in[2]), "");
/* Test newv_with_properties && getv */
test_obj = (TestObject *) g_object_new_with_properties (
test_object_get_type (), 0, NULL, NULL);
g_assert_nonnull (properties[PROP_FOO]);
n.pspec[0] = properties[PROP_BAZ];
n.pspec[1] = properties[PROP_BAR];
n.pspec[2] = properties[PROP_FOO];
n.pos = 0;
g_signal_connect (test_obj, "notify", G_CALLBACK (on_notify2), &n);
g_object_freeze_notify (G_OBJECT (test_obj));
{
g_object_setv (G_OBJECT (test_obj), 3, prop_names, values_in);
/* Set "foo" to 70 */
g_value_set_int (&(values_in[0]), 100);
g_object_setv (G_OBJECT (test_obj), 1, prop_names, values_in);
}
g_object_thaw_notify (G_OBJECT (test_obj));
g_assert_cmpint (n.pos, ==, 3);
for (i = 0; i < 3; i++)
g_value_unset (&values_in[i]);
g_object_unref (test_obj);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/properties/install", properties_install);
g_test_add_func ("/properties/notify", properties_notify);
g_test_add_func ("/properties/notify-queue", properties_notify_queue);
g_test_add_func ("/properties/construct", properties_construct);
g_test_add_func ("/properties/get-property", properties_get_property);
g_test_add_func ("/properties/testv_with_no_properties",
properties_testv_with_no_properties);
g_test_add_func ("/properties/testv_with_valid_properties",
properties_testv_with_valid_properties);
g_test_add_func ("/properties/testv_with_invalid_property_type",
properties_testv_with_invalid_property_type);
g_test_add_func ("/properties/testv_with_invalid_property_names",
properties_testv_with_invalid_property_names);
g_test_add_func ("/properties/testv_getv", properties_testv_getv);
g_test_add_func ("/properties/testv_notify_queue",
properties_testv_notify_queue);
return g_test_run ();
}

123
gobject/tests/qdata.c Normal file
View file

@ -0,0 +1,123 @@
/*
* Copyright 2012 Red Hat, Inc.
*
* This program 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.1 of the License, or (at your option) any later version.
*
* See the included COPYING file for more information.
*/
#include <glib-object.h>
gboolean fail;
#define THREADS 10
#define ROUNDS 10000
GObject *object;
gint bucket[THREADS]; /* accessed from multiple threads, but should never be contested due to the sequence of thread operations */
static gpointer
thread_func (gpointer data)
{
gint idx = GPOINTER_TO_INT (data);
gint i;
gint d;
gint value;
gint new_value;
for (i = 0; i < ROUNDS; i++)
{
d = g_random_int_range (-10, 100);
bucket[idx] += d;
retry:
value = GPOINTER_TO_INT (g_object_get_data (object, "test"));
new_value = value + d;
if (fail)
g_object_set_data (object, "test", GINT_TO_POINTER (new_value));
else
{
if (!g_object_replace_data (object, "test",
GINT_TO_POINTER (value),
GINT_TO_POINTER (new_value),
NULL, NULL))
goto retry;
}
g_thread_yield ();
}
return NULL;
}
static void
test_qdata_threaded (void)
{
gint sum;
gint i;
GThread *threads[THREADS];
gint result;
object = g_object_new (G_TYPE_OBJECT, NULL);
g_object_set_data (object, "test", GINT_TO_POINTER (0));
for (i = 0; i < THREADS; i++)
bucket[i] = 0;
for (i = 0; i < THREADS; i++)
threads[i] = g_thread_new ("qdata", thread_func, GINT_TO_POINTER (i));
for (i = 0; i < THREADS; i++)
g_thread_join (threads[i]);
sum = 0;
for (i = 0; i < THREADS; i++)
sum += bucket[i];
result = GPOINTER_TO_INT (g_object_get_data (object, "test"));
g_assert_cmpint (sum, ==, result);
g_object_unref (object);
}
static void
test_qdata_dup (void)
{
gchar *s, *s2;
GQuark quark;
gboolean b;
quark = g_quark_from_static_string ("test");
object = g_object_new (G_TYPE_OBJECT, NULL);
s = g_strdup ("s");
g_object_set_qdata_full (object, quark, s, g_free);
s2 = g_object_dup_qdata (object, quark, (GDuplicateFunc)g_strdup, NULL);
g_assert_cmpstr (s, ==, s2);
g_assert (s != s2);
g_free (s2);
b = g_object_replace_qdata (object, quark, s, "s2", NULL, NULL);
g_assert (b);
g_free (s);
g_object_unref (object);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
fail = !!g_getenv ("FAIL");
g_test_add_func ("/qdata/threaded", test_qdata_threaded);
g_test_add_func ("/qdata/dup", test_qdata_dup);
return g_test_run ();
}

973
gobject/tests/reference.c Normal file
View file

@ -0,0 +1,973 @@
#include <glib-object.h>
static void
test_fundamentals (void)
{
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_NONE));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_INTERFACE));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_CHAR));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_UCHAR));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_BOOLEAN));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_INT));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_UINT));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_LONG));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_ULONG));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_INT64));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_UINT64));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_ENUM));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_FLAGS));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_FLOAT));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_DOUBLE));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_STRING));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_POINTER));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_BOXED));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_PARAM));
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_OBJECT));
g_assert (G_TYPE_OBJECT == g_object_get_type ());
g_assert (G_TYPE_IS_FUNDAMENTAL (G_TYPE_VARIANT));
g_assert (G_TYPE_IS_DERIVED (G_TYPE_INITIALLY_UNOWNED));
g_assert (g_type_fundamental_next () == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST));
}
static void
test_type_qdata (void)
{
gchar *data;
g_type_set_qdata (G_TYPE_ENUM, g_quark_from_string ("bla"), "bla");
data = g_type_get_qdata (G_TYPE_ENUM, g_quark_from_string ("bla"));
g_assert_cmpstr (data, ==, "bla");
}
static void
test_type_query (void)
{
GTypeQuery query;
g_type_query (G_TYPE_ENUM, &query);
g_assert_cmpint (query.type, ==, G_TYPE_ENUM);
g_assert_cmpstr (query.type_name, ==, "GEnum");
g_assert_cmpint (query.class_size, ==, sizeof (GEnumClass));
g_assert_cmpint (query.instance_size, ==, 0);
}
typedef struct _MyObject MyObject;
typedef struct _MyObjectClass MyObjectClass;
typedef struct _MyObjectClassPrivate MyObjectClassPrivate;
struct _MyObject
{
GObject parent_instance;
gint count;
};
struct _MyObjectClass
{
GObjectClass parent_class;
};
struct _MyObjectClassPrivate
{
gint secret_class_count;
};
static GType my_object_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyObject, my_object, G_TYPE_OBJECT,
g_type_add_class_private (g_define_type_id, sizeof (MyObjectClassPrivate)) );
static void
my_object_init (MyObject *obj)
{
obj->count = 42;
}
static void
my_object_class_init (MyObjectClass *klass)
{
}
static void
test_class_private (void)
{
GObject *obj;
MyObjectClass *class;
MyObjectClassPrivate *priv;
obj = g_object_new (my_object_get_type (), NULL);
class = g_type_class_ref (my_object_get_type ());
priv = G_TYPE_CLASS_GET_PRIVATE (class, my_object_get_type (), MyObjectClassPrivate);
priv->secret_class_count = 13;
g_type_class_unref (class);
g_object_unref (obj);
g_assert_cmpint (g_type_qname (my_object_get_type ()), ==, g_quark_from_string ("MyObject"));
}
static void
test_clear (void)
{
GObject *o = NULL;
GObject *tmp;
g_clear_object (&o);
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (tmp->ref_count, ==, 1);
o = g_object_ref (tmp);
g_assert (o != NULL);
g_assert_cmpint (tmp->ref_count, ==, 2);
g_clear_object (&o);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert (o == NULL);
g_object_unref (tmp);
}
static void
test_clear_function (void)
{
GObject *o = NULL;
GObject *tmp;
(g_clear_object) (&o);
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (tmp->ref_count, ==, 1);
o = g_object_ref (tmp);
g_assert (o != NULL);
g_assert_cmpint (tmp->ref_count, ==, 2);
(g_clear_object) (&o);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert (o == NULL);
g_object_unref (tmp);
}
static void
test_set (void)
{
GObject *o = NULL;
GObject *tmp;
gpointer tmp_weak = NULL;
g_assert (!g_set_object (&o, NULL));
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
tmp_weak = tmp;
g_object_add_weak_pointer (tmp, &tmp_weak);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert (g_set_object (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
/* Setting it again shouldnt cause finalisation. */
g_assert (!g_set_object (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert_nonnull (tmp_weak);
g_assert (g_set_object (&o, NULL));
g_assert (o == NULL);
g_assert_null (tmp_weak);
}
static void
test_set_function (void)
{
GObject *o = NULL;
GObject *tmp;
gpointer tmp_weak = NULL;
g_assert (!(g_set_object) (&o, NULL));
g_assert (o == NULL);
tmp = g_object_new (G_TYPE_OBJECT, NULL);
tmp_weak = tmp;
g_object_add_weak_pointer (tmp, &tmp_weak);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert ((g_set_object) (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
/* Setting it again shouldnt cause finalisation. */
g_assert (!(g_set_object) (&o, tmp));
g_assert (o == tmp);
g_assert_cmpint (tmp->ref_count, ==, 1);
g_assert_nonnull (tmp_weak);
g_assert ((g_set_object) (&o, NULL));
g_assert (o == NULL);
g_assert_null (tmp_weak);
}
static void
test_set_derived_type (void)
{
GBinding *obj = NULL;
GObject *o = NULL;
GBinding *b = NULL;
g_test_summary ("Check that g_set_object() doesnt give strict aliasing "
"warnings when used on types derived from GObject");
g_assert_false (g_set_object (&o, NULL));
g_assert_null (o);
g_assert_false (g_set_object (&b, NULL));
g_assert_null (b);
obj = g_object_new (my_object_get_type (), NULL);
g_assert_true (g_set_object (&o, G_OBJECT (obj)));
g_assert_true (o == G_OBJECT (obj));
g_assert_true (g_set_object (&b, obj));
g_assert_true (b == obj);
g_object_unref (obj);
g_clear_object (&b);
g_clear_object (&o);
}
static void
toggle_cb (gpointer data, GObject *obj, gboolean is_last)
{
gboolean *b = data;
*b = TRUE;
}
static void
test_object_value (void)
{
GObject *v;
GObject *v2;
GValue value = G_VALUE_INIT;
gboolean toggled = FALSE;
g_value_init (&value, G_TYPE_OBJECT);
v = g_object_new (G_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (v, toggle_cb, &toggled);
g_value_take_object (&value, v);
v2 = g_value_get_object (&value);
g_assert (v2 == v);
v2 = g_value_dup_object (&value);
g_assert (v2 == v); /* objects use ref/unref for copy/free */
g_object_unref (v2);
g_assert (!toggled);
g_value_unset (&value);
g_assert (toggled);
/* test the deprecated variant too */
g_value_init (&value, G_TYPE_OBJECT);
/* get a new reference */
g_object_ref (v);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
g_value_set_object_take_ownership (&value, v);
G_GNUC_END_IGNORE_DEPRECATIONS
toggled = FALSE;
g_value_unset (&value);
g_assert (toggled);
g_object_remove_toggle_ref (v, toggle_cb, &toggled);
}
static void
test_initially_unowned (void)
{
GObject *obj;
obj = g_object_new (G_TYPE_INITIALLY_UNOWNED, NULL);
g_assert (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_ref_sink (obj);
g_assert (!g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_ref_sink (obj);
g_assert (!g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (obj);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_force_floating (obj);
g_assert (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_ref_sink (obj);
g_object_unref (obj);
obj = g_object_new (G_TYPE_INITIALLY_UNOWNED, NULL);
g_assert_true (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_take_ref (obj);
g_assert_false (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_take_ref (obj);
g_assert_false (g_object_is_floating (obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_unref (obj);
}
static void
test_weak_pointer (void)
{
GObject *obj;
gpointer weak;
gpointer weak2;
weak = weak2 = obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_add_weak_pointer (obj, &weak);
g_object_add_weak_pointer (obj, &weak2);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert (weak == obj);
g_assert (weak2 == obj);
g_object_remove_weak_pointer (obj, &weak2);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert (weak == obj);
g_assert (weak2 == obj);
g_object_unref (obj);
g_assert (weak == NULL);
g_assert (weak2 == obj);
}
static void
test_weak_pointer_clear (void)
{
GObject *obj;
gpointer weak = NULL;
g_clear_weak_pointer (&weak);
g_assert_null (weak);
weak = obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_add_weak_pointer (obj, &weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_clear_weak_pointer (&weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_object_unref (obj);
}
static void
test_weak_pointer_clear_function (void)
{
GObject *obj;
gpointer weak = NULL;
(g_clear_weak_pointer) (&weak);
g_assert_null (weak);
weak = obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_object_add_weak_pointer (obj, &weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
(g_clear_weak_pointer) (&weak);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_object_unref (obj);
}
static void
test_weak_pointer_set (void)
{
GObject *obj;
gpointer weak = NULL;
g_assert_false (g_set_weak_pointer (&weak, NULL));
g_assert_null (weak);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (g_set_weak_pointer (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_assert_true (g_set_weak_pointer (&weak, NULL));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_assert_true (g_set_weak_pointer (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_object_unref (obj);
g_assert_null (weak);
}
static void
test_weak_pointer_set_function (void)
{
GObject *obj;
gpointer weak = NULL;
g_assert_false ((g_set_weak_pointer) (&weak, NULL));
g_assert_null (weak);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true ((g_set_weak_pointer) (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_assert_true ((g_set_weak_pointer) (&weak, NULL));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_null (weak);
g_assert_true ((g_set_weak_pointer) (&weak, obj));
g_assert_cmpint (obj->ref_count, ==, 1);
g_assert_true (weak == obj);
g_object_unref (obj);
g_assert_null (weak);
}
/* See gobject/tests/threadtests.c for the threaded version */
static void
test_weak_ref (void)
{
GObject *obj;
GObject *obj2;
GObject *tmp;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
GWeakRef weak2 = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
GWeakRef weak3 = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
GWeakRef *dynamic_weak = g_new (GWeakRef, 1);
/* you can initialize to empty like this... */
g_weak_ref_init (&weak2, NULL);
g_assert (g_weak_ref_get (&weak2) == NULL);
/* ... or via an initializer */
g_weak_ref_init (&weak3, NULL);
g_assert (g_weak_ref_get (&weak3) == NULL);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj->ref_count, ==, 1);
obj2 = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_cmpint (obj2->ref_count, ==, 1);
/* you can init with an object (even if uninitialized) */
g_weak_ref_init (&weak, obj);
g_weak_ref_init (dynamic_weak, obj);
/* or set to point at an object, if initialized (maybe to 0) */
g_weak_ref_set (&weak2, obj);
g_weak_ref_set (&weak3, obj);
/* none of this affects its refcount */
g_assert_cmpint (obj->ref_count, ==, 1);
/* getting the value takes a ref */
tmp = g_weak_ref_get (&weak);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
tmp = g_weak_ref_get (&weak2);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
tmp = g_weak_ref_get (&weak3);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
tmp = g_weak_ref_get (dynamic_weak);
g_assert (tmp == obj);
g_assert_cmpint (obj->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj->ref_count, ==, 1);
/* clearing a weak ref stops tracking */
g_weak_ref_clear (&weak);
/* setting a weak ref to NULL stops tracking too */
g_weak_ref_set (&weak2, NULL);
g_assert (g_weak_ref_get (&weak2) == NULL);
g_weak_ref_clear (&weak2);
/* setting a weak ref to a new object stops tracking the old one */
g_weak_ref_set (dynamic_weak, obj2);
tmp = g_weak_ref_get (dynamic_weak);
g_assert (tmp == obj2);
g_assert_cmpint (obj2->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj2->ref_count, ==, 1);
g_assert_cmpint (obj->ref_count, ==, 1);
/* free the object: weak3 is the only one left pointing there */
g_object_unref (obj);
g_assert (g_weak_ref_get (&weak3) == NULL);
/* setting a weak ref to a new object stops tracking the old one */
g_weak_ref_set (dynamic_weak, obj2);
tmp = g_weak_ref_get (dynamic_weak);
g_assert (tmp == obj2);
g_assert_cmpint (obj2->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj2->ref_count, ==, 1);
g_weak_ref_clear (&weak3);
/* unset dynamic_weak... */
g_weak_ref_set (dynamic_weak, NULL);
g_assert_null (g_weak_ref_get (dynamic_weak));
/* initializing a weak reference to an object that had before works */
g_weak_ref_set (dynamic_weak, obj2);
tmp = g_weak_ref_get (dynamic_weak);
g_assert_true (tmp == obj2);
g_assert_cmpint (obj2->ref_count, ==, 2);
g_object_unref (tmp);
g_assert_cmpint (obj2->ref_count, ==, 1);
/* clear and free dynamic_weak... */
g_weak_ref_clear (dynamic_weak);
/* ... to prove that doing so stops this from being a use-after-free */
g_object_unref (obj2);
g_free (dynamic_weak);
}
G_DECLARE_FINAL_TYPE (WeakReffedObject, weak_reffed_object,
WEAK, REFFED_OBJECT, GObject)
struct _WeakReffedObject
{
GObject parent;
GWeakRef *weak_ref;
};
G_DEFINE_TYPE (WeakReffedObject, weak_reffed_object, G_TYPE_OBJECT)
static void
weak_reffed_object_dispose (GObject *object)
{
WeakReffedObject *weak_reffed = WEAK_REFFED_OBJECT (object);
g_assert_cmpint (object->ref_count, ==, 1);
g_weak_ref_set (weak_reffed->weak_ref, object);
G_OBJECT_CLASS (weak_reffed_object_parent_class)->dispose (object);
g_assert_null (g_weak_ref_get (weak_reffed->weak_ref));
}
static void
weak_reffed_object_init (WeakReffedObject *connector)
{
}
static void
weak_reffed_object_class_init (WeakReffedObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = weak_reffed_object_dispose;
}
static void
test_weak_ref_on_dispose (void)
{
WeakReffedObject *obj;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
g_test_summary ("Test that a weak ref set during dispose vfunc is cleared");
g_weak_ref_init (&weak, NULL);
obj = g_object_new (weak_reffed_object_get_type (), NULL);
obj->weak_ref = &weak;
g_assert_cmpint (G_OBJECT (obj)->ref_count, ==, 1);
g_clear_object (&obj);
g_assert_null (g_weak_ref_get (&weak));
}
static void
test_weak_ref_on_run_dispose (void)
{
GObject *obj;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/865");
g_test_summary ("Test that a weak ref is cleared on g_object_run_dispose()");
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_weak_ref_init (&weak, obj);
g_assert_true (obj == g_weak_ref_get (&weak));
g_object_unref (obj);
g_object_run_dispose (obj);
g_assert_null (g_weak_ref_get (&weak));
g_clear_object (&obj);
g_assert_null (g_weak_ref_get (&weak));
}
static void
on_weak_ref_toggle_notify (gpointer data,
GObject *object,
gboolean is_last_ref)
{
GWeakRef *weak = data;
if (is_last_ref)
g_weak_ref_set (weak, object);
}
static void
on_weak_ref_toggle_notify_disposed (gpointer data,
GObject *object)
{
g_assert_cmpint (object->ref_count, ==, 1);
g_object_ref (object);
g_object_unref (object);
}
static void
test_weak_ref_on_toggle_notify (void)
{
GObject *obj;
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
g_test_summary ("Test that a weak ref set on toggle notify is cleared");
g_weak_ref_init (&weak, NULL);
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (obj, on_weak_ref_toggle_notify, &weak);
g_object_weak_ref (obj, on_weak_ref_toggle_notify_disposed, NULL);
g_object_unref (obj);
g_assert_cmpint (obj->ref_count, ==, 1);
g_clear_object (&obj);
g_assert_null (g_weak_ref_get (&weak));
}
typedef struct
{
gboolean should_be_last;
gint count;
} Count;
static void
toggle_notify (gpointer data,
GObject *obj,
gboolean is_last)
{
Count *c = data;
g_assert (is_last == c->should_be_last);
c->count++;
}
static void
test_toggle_ref (void)
{
GObject *obj;
Count c, c2;
obj = g_object_new (G_TYPE_OBJECT, NULL);
g_object_add_toggle_ref (obj, toggle_notify, &c);
g_object_add_toggle_ref (obj, toggle_notify, &c2);
c.should_be_last = c2.should_be_last = TRUE;
c.count = c2.count = 0;
g_object_unref (obj);
g_assert_cmpint (c.count, ==, 0);
g_assert_cmpint (c2.count, ==, 0);
g_object_ref (obj);
g_assert_cmpint (c.count, ==, 0);
g_assert_cmpint (c2.count, ==, 0);
g_object_remove_toggle_ref (obj, toggle_notify, &c2);
g_object_unref (obj);
g_assert_cmpint (c.count, ==, 1);
c.should_be_last = FALSE;
g_object_ref (obj);
g_assert_cmpint (c.count, ==, 2);
c.should_be_last = TRUE;
g_object_unref (obj);
g_assert_cmpint (c.count, ==, 3);
g_object_remove_toggle_ref (obj, toggle_notify, &c);
}
static gboolean global_destroyed;
static gint global_value;
static void
data_destroy (gpointer data)
{
g_assert_cmpint (GPOINTER_TO_INT (data), ==, global_value);
global_destroyed = TRUE;
}
static void
test_object_qdata (void)
{
GObject *obj;
gpointer v;
GQuark quark;
obj = g_object_new (G_TYPE_OBJECT, NULL);
global_value = 1;
global_destroyed = FALSE;
g_object_set_data_full (obj, "test", GINT_TO_POINTER (1), data_destroy);
v = g_object_get_data (obj, "test");
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 1);
g_object_set_data_full (obj, "test", GINT_TO_POINTER (2), data_destroy);
g_assert (global_destroyed);
global_value = 2;
global_destroyed = FALSE;
v = g_object_steal_data (obj, "test");
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 2);
g_assert (!global_destroyed);
global_value = 1;
global_destroyed = FALSE;
quark = g_quark_from_string ("test");
g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (1), data_destroy);
v = g_object_get_qdata (obj, quark);
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 1);
g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (2), data_destroy);
g_assert (global_destroyed);
global_value = 2;
global_destroyed = FALSE;
v = g_object_steal_qdata (obj, quark);
g_assert_cmpint (GPOINTER_TO_INT (v), ==, 2);
g_assert (!global_destroyed);
g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (3), data_destroy);
global_value = 3;
global_destroyed = FALSE;
g_object_unref (obj);
g_assert (global_destroyed);
}
typedef struct {
const gchar *value;
gint refcount;
} Value;
static gpointer
ref_value (gpointer value, gpointer user_data)
{
Value *v = value;
Value **old_value_p = user_data;
if (old_value_p)
*old_value_p = v;
if (v)
v->refcount += 1;
return value;
}
static void
unref_value (gpointer value)
{
Value *v = value;
v->refcount -= 1;
if (v->refcount == 0)
g_free (value);
}
static
Value *
new_value (const gchar *s)
{
Value *v;
v = g_new (Value, 1);
v->value = s;
v->refcount = 1;
return v;
}
static void
test_object_qdata2 (void)
{
GObject *obj;
Value *v, *v1, *v2, *v3, *old_val;
GDestroyNotify old_destroy;
gboolean res;
obj = g_object_new (G_TYPE_OBJECT, NULL);
v1 = new_value ("bla");
g_object_set_data_full (obj, "test", v1, unref_value);
v = g_object_get_data (obj, "test");
g_assert_cmpstr (v->value, ==, "bla");
g_assert_cmpint (v->refcount, ==, 1);
v = g_object_dup_data (obj, "test", ref_value, &old_val);
g_assert (old_val == v1);
g_assert_cmpstr (v->value, ==, "bla");
g_assert_cmpint (v->refcount, ==, 2);
unref_value (v);
v = g_object_dup_data (obj, "nono", ref_value, &old_val);
g_assert (old_val == NULL);
g_assert (v == NULL);
v2 = new_value ("not");
res = g_object_replace_data (obj, "test", v1, v2, unref_value, &old_destroy);
g_assert (res == TRUE);
g_assert (old_destroy == unref_value);
g_assert_cmpstr (v1->value, ==, "bla");
g_assert_cmpint (v1->refcount, ==, 1);
v = g_object_get_data (obj, "test");
g_assert_cmpstr (v->value, ==, "not");
g_assert_cmpint (v->refcount, ==, 1);
v3 = new_value ("xyz");
res = g_object_replace_data (obj, "test", v1, v3, unref_value, &old_destroy);
g_assert (res == FALSE);
g_assert_cmpstr (v2->value, ==, "not");
g_assert_cmpint (v2->refcount, ==, 1);
unref_value (v1);
res = g_object_replace_data (obj, "test", NULL, v3, unref_value, &old_destroy);
g_assert (res == FALSE);
g_assert_cmpstr (v2->value, ==, "not");
g_assert_cmpint (v2->refcount, ==, 1);
res = g_object_replace_data (obj, "test", v2, NULL, unref_value, &old_destroy);
g_assert (res == TRUE);
g_assert (old_destroy == unref_value);
g_assert_cmpstr (v2->value, ==, "not");
g_assert_cmpint (v2->refcount, ==, 1);
unref_value (v2);
v = g_object_get_data (obj, "test");
g_assert (v == NULL);
res = g_object_replace_data (obj, "test", NULL, v3, unref_value, &old_destroy);
g_assert (res == TRUE);
v = g_object_get_data (obj, "test");
g_assert (v == v3);
ref_value (v3, NULL);
g_assert_cmpint (v3->refcount, ==, 2);
g_object_unref (obj);
g_assert_cmpint (v3->refcount, ==, 1);
unref_value (v3);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/type/fundamentals", test_fundamentals);
g_test_add_func ("/type/qdata", test_type_qdata);
g_test_add_func ("/type/query", test_type_query);
g_test_add_func ("/type/class-private", test_class_private);
g_test_add_func ("/object/clear", test_clear);
g_test_add_func ("/object/clear-function", test_clear_function);
g_test_add_func ("/object/set", test_set);
g_test_add_func ("/object/set-function", test_set_function);
g_test_add_func ("/object/set/derived-type", test_set_derived_type);
g_test_add_func ("/object/value", test_object_value);
g_test_add_func ("/object/initially-unowned", test_initially_unowned);
g_test_add_func ("/object/weak-pointer", test_weak_pointer);
g_test_add_func ("/object/weak-pointer/clear", test_weak_pointer_clear);
g_test_add_func ("/object/weak-pointer/clear-function", test_weak_pointer_clear_function);
g_test_add_func ("/object/weak-pointer/set", test_weak_pointer_set);
g_test_add_func ("/object/weak-pointer/set-function", test_weak_pointer_set_function);
g_test_add_func ("/object/weak-ref", test_weak_ref);
g_test_add_func ("/object/weak-ref/on-dispose", test_weak_ref_on_dispose);
g_test_add_func ("/object/weak-ref/on-run-dispose", test_weak_ref_on_run_dispose);
g_test_add_func ("/object/weak-ref/on-toggle-notify", test_weak_ref_on_toggle_notify);
g_test_add_func ("/object/toggle-ref", test_toggle_ref);
g_test_add_func ("/object/qdata", test_object_qdata);
g_test_add_func ("/object/qdata2", test_object_qdata2);
return g_test_run ();
}

View file

@ -0,0 +1,299 @@
#include <glib-object.h>
typedef struct {
GObject instance;
} MyObj;
typedef struct {
GObjectClass parent_class;
} MyObjClass;
enum {
SIGNAL1,
SIGNAL2,
LAST_SIGNAL
};
guint signals[LAST_SIGNAL];
GType my_obj_get_type (void);
G_DEFINE_TYPE (MyObj, my_obj, G_TYPE_OBJECT)
static void
my_obj_init (MyObj *o)
{
}
static void
my_obj_class_init (MyObjClass *class)
{
signals[SIGNAL1] =
g_signal_new ("signal1",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
signals[SIGNAL2] =
g_signal_new ("signal2",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
}
static void
nop (void)
{
}
#define HANDLERS 500000
static void
test_connect_many (void)
{
MyObj *o;
gdouble time_elapsed;
gint i;
o = g_object_new (my_obj_get_type (), NULL);
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "connected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_many_ordered (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gdouble time_elapsed;
gint i;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_many_inverse (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gdouble time_elapsed;
gint i;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
g_test_timer_start ();
for (i = HANDLERS - 1; i >= 0; i--)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_many_random (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_2_signals (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
{
if (i % 2 == 0)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
else
handlers[i] = g_signal_connect (o, "signal2", G_CALLBACK (nop), NULL);
}
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_disconnect_2_objects (void)
{
MyObj *o1, *o2, *o;
gulong handlers[HANDLERS];
MyObj *objects[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o1 = g_object_new (my_obj_get_type (), NULL);
o2 = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
{
if (i % 2 == 0)
{
handlers[i] = g_signal_connect (o1, "signal1", G_CALLBACK (nop), NULL);
objects[i] = o1;
}
else
{
handlers[i] = g_signal_connect (o2, "signal1", G_CALLBACK (nop), NULL);
objects[i] = o2;
}
}
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
o = objects[i];
objects[i] = objects[j];
objects[j] = o;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_disconnect (objects[i], handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o1);
g_object_unref (o2);
g_test_minimized_result (time_elapsed, "disconnected %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
static void
test_block_many (void)
{
MyObj *o;
gulong handlers[HANDLERS];
gulong id;
gdouble time_elapsed;
gint i, j;
o = g_object_new (my_obj_get_type (), NULL);
for (i = 0; i < HANDLERS; i++)
handlers[i] = g_signal_connect (o, "signal1", G_CALLBACK (nop), NULL);
for (i = 0; i < HANDLERS; i++)
{
j = g_test_rand_int_range (0, HANDLERS);
id = handlers[i];
handlers[i] = handlers[j];
handlers[j] = id;
}
g_test_timer_start ();
for (i = 0; i < HANDLERS; i++)
g_signal_handler_block (o, handlers[i]);
for (i = HANDLERS - 1; i >= 0; i--)
g_signal_handler_unblock (o, handlers[i]);
time_elapsed = g_test_timer_elapsed ();
g_object_unref (o);
g_test_minimized_result (time_elapsed, "blocked and unblocked %u handlers in %6.3f seconds", HANDLERS, time_elapsed);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
if (g_test_perf ())
{
g_test_add_func ("/signal/handler/connect-many", test_connect_many);
g_test_add_func ("/signal/handler/disconnect-many-ordered", test_disconnect_many_ordered);
g_test_add_func ("/signal/handler/disconnect-many-inverse", test_disconnect_many_inverse);
g_test_add_func ("/signal/handler/disconnect-many-random", test_disconnect_many_random);
g_test_add_func ("/signal/handler/disconnect-2-signals", test_disconnect_2_signals);
g_test_add_func ("/signal/handler/disconnect-2-objects", test_disconnect_2_objects);
g_test_add_func ("/signal/handler/block-many", test_block_many);
}
return g_test_run ();
}

650
gobject/tests/signalgroup.c Normal file
View file

@ -0,0 +1,650 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
*
* Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
* Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
*
* 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.1 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include <glib-object.h>
G_DECLARE_FINAL_TYPE (SignalTarget, signal_target, TEST, SIGNAL_TARGET, GObject)
struct _SignalTarget
{
GObject parent_instance;
};
G_DEFINE_TYPE (SignalTarget, signal_target, G_TYPE_OBJECT)
static G_DEFINE_QUARK (detail, signal_detail);
enum {
THE_SIGNAL,
NEVER_EMITTED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void
signal_target_class_init (SignalTargetClass *klass)
{
signals[THE_SIGNAL] =
g_signal_new ("the-signal",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
signals[NEVER_EMITTED] =
g_signal_new ("never-emitted",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_OBJECT);
}
static void
signal_target_init (SignalTarget *self)
{
}
static gint global_signal_calls;
static gint global_weak_notify_called;
static void
connect_before_cb (SignalTarget *target,
GSignalGroup *group,
gint *signal_calls)
{
SignalTarget *readback;
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_nonnull (signal_calls);
g_assert_true (signal_calls == &global_signal_calls);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
*signal_calls += 1;
}
static void
connect_after_cb (SignalTarget *target,
GSignalGroup *group,
gint *signal_calls)
{
SignalTarget *readback;
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_nonnull (signal_calls);
g_assert_true (signal_calls == &global_signal_calls);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_cmpint (*signal_calls, ==, 4);
*signal_calls += 1;
}
static void
connect_swapped_cb (gint *signal_calls,
GSignalGroup *group,
SignalTarget *target)
{
SignalTarget *readback;
g_assert_true (signal_calls != NULL);
g_assert_true (signal_calls == &global_signal_calls);
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
*signal_calls += 1;
}
static void
connect_object_cb (SignalTarget *target,
GSignalGroup *group,
GObject *object)
{
SignalTarget *readback;
gint *signal_calls;
g_assert_true (TEST_IS_SIGNAL_TARGET (target));
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_true (G_IS_OBJECT (object));
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
signal_calls = g_object_get_data (object, "signal-calls");
g_assert_nonnull (signal_calls);
g_assert_true (signal_calls == &global_signal_calls);
*signal_calls += 1;
}
static void
connect_bad_detail_cb (SignalTarget *target,
GSignalGroup *group,
GObject *object)
{
g_error ("This detailed signal is never emitted!");
}
static void
connect_never_emitted_cb (SignalTarget *target,
gboolean *weak_notify_called)
{
g_error ("This signal is never emitted!");
}
static void
connect_data_notify_cb (gboolean *weak_notify_called,
GClosure *closure)
{
g_assert_nonnull (weak_notify_called);
g_assert_true (weak_notify_called == &global_weak_notify_called);
g_assert_nonnull (closure);
g_assert_false (*weak_notify_called);
*weak_notify_called = TRUE;
}
static void
connect_data_weak_notify_cb (gboolean *weak_notify_called,
GSignalGroup *group)
{
g_assert_nonnull (weak_notify_called);
g_assert_true (weak_notify_called == &global_weak_notify_called);
g_assert_true (G_IS_SIGNAL_GROUP (group));
g_assert_true (*weak_notify_called);
}
static void
connect_all_signals (GSignalGroup *group)
{
GObject *object;
/* Check that these are called in the right order */
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (connect_before_cb),
&global_signal_calls);
g_signal_group_connect_after (group,
"the-signal",
G_CALLBACK (connect_after_cb),
&global_signal_calls);
/* Check that this is called with the arguments swapped */
g_signal_group_connect_swapped (group,
"the-signal",
G_CALLBACK (connect_swapped_cb),
&global_signal_calls);
/* Check that this is called with the arguments swapped */
object = g_object_new (G_TYPE_OBJECT, NULL);
g_object_set_data (object, "signal-calls", &global_signal_calls);
g_signal_group_connect_object (group,
"the-signal",
G_CALLBACK (connect_object_cb),
object,
0);
g_object_weak_ref (G_OBJECT (group),
(GWeakNotify)g_object_unref,
object);
/* Check that a detailed signal is handled correctly */
g_signal_group_connect (group,
"the-signal::detail",
G_CALLBACK (connect_before_cb),
&global_signal_calls);
g_signal_group_connect (group,
"the-signal::bad-detail",
G_CALLBACK (connect_bad_detail_cb),
NULL);
/* Check that the notify is called correctly */
global_weak_notify_called = FALSE;
g_signal_group_connect_data (group,
"never-emitted",
G_CALLBACK (connect_never_emitted_cb),
&global_weak_notify_called,
(GClosureNotify)connect_data_notify_cb,
0);
g_object_weak_ref (G_OBJECT (group),
(GWeakNotify)connect_data_weak_notify_cb,
&global_weak_notify_called);
}
static void
assert_signals (SignalTarget *target,
GSignalGroup *group,
gboolean success)
{
g_assert (TEST_IS_SIGNAL_TARGET (target));
g_assert (group == NULL || G_IS_SIGNAL_GROUP (group));
global_signal_calls = 0;
g_signal_emit (target, signals[THE_SIGNAL],
signal_detail_quark (), group);
g_assert_cmpint (global_signal_calls, ==, success ? 5 : 0);
}
static void
dummy_handler (void)
{
}
static void
test_signal_group_invalid (void)
{
GObject *invalid_target = g_object_new (G_TYPE_OBJECT, NULL);
SignalTarget *target = g_object_new (signal_target_get_type (), NULL);
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
/* Invalid Target Type */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*g_type_is_a*G_TYPE_OBJECT*");
g_signal_group_new (G_TYPE_DATE_TIME);
g_test_assert_expected_messages ();
/* Invalid Target */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Failed to set GSignalGroup of target type SignalTarget using target * of type GObject*");
g_signal_group_set_target (group, invalid_target);
g_assert_finalize_object (group);
g_test_assert_expected_messages ();
/* Invalid Signal Name */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*g_signal_parse_name*");
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_connect (group,
"does-not-exist",
G_CALLBACK (connect_before_cb),
NULL);
g_test_assert_expected_messages ();
g_assert_finalize_object (group);
/* Invalid Callback */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*c_handler != NULL*");
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (NULL),
NULL);
g_test_assert_expected_messages ();
g_assert_finalize_object (group);
/* Connecting after setting target */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
"*Cannot add signals after setting target*");
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_set_target (group, target);
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (dummy_handler),
NULL);
g_test_assert_expected_messages ();
g_assert_finalize_object (group);
g_assert_finalize_object (target);
g_assert_finalize_object (invalid_target);
}
static void
test_signal_group_simple (void)
{
SignalTarget *target;
GSignalGroup *group;
SignalTarget *readback;
/* Set the target before connecting the signals */
group = g_signal_group_new (signal_target_get_type ());
target = g_object_new (signal_target_get_type (), NULL);
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (group);
assert_signals (target, NULL, FALSE);
g_assert_finalize_object (target);
group = g_signal_group_new (signal_target_get_type ());
target = g_object_new (signal_target_get_type (), NULL);
connect_all_signals (group);
g_signal_group_set_target (group, target);
assert_signals (target, group, TRUE);
g_assert_finalize_object (target);
g_assert_finalize_object (group);
}
static void
test_signal_group_changing_target (void)
{
SignalTarget *target1, *target2;
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
SignalTarget *readback;
connect_all_signals (group);
g_assert_null (g_signal_group_dup_target (group));
/* Set the target after connecting the signals */
target1 = g_object_new (signal_target_get_type (), NULL);
g_signal_group_set_target (group, target1);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
assert_signals (target1, group, TRUE);
/* Set the same target */
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
g_signal_group_set_target (group, target1);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
assert_signals (target1, group, TRUE);
/* Set a new target when the current target is non-NULL */
target2 = g_object_new (signal_target_get_type (), NULL);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
g_signal_group_set_target (group, target2);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target2);
g_object_unref (readback);
assert_signals (target2, group, TRUE);
g_assert_finalize_object (target2);
g_assert_finalize_object (target1);
g_assert_finalize_object (group);
}
static void
assert_blocking (SignalTarget *target,
GSignalGroup *group,
gint count)
{
gint i;
assert_signals (target, group, TRUE);
/* Assert that multiple blocks are effective */
for (i = 0; i < count; ++i)
{
g_signal_group_block (group);
assert_signals (target, group, FALSE);
}
/* Assert that the signal is not emitted after the first unblock */
for (i = 0; i < count; ++i)
{
assert_signals (target, group, FALSE);
g_signal_group_unblock (group);
}
assert_signals (target, group, TRUE);
}
static void
test_signal_group_blocking (void)
{
SignalTarget *target1, *target2, *readback;
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
/* Test blocking and unblocking null target */
g_signal_group_block (group);
g_signal_group_unblock (group);
connect_all_signals (group);
g_assert_null (g_signal_group_dup_target (group));
target1 = g_object_new (signal_target_get_type (), NULL);
g_signal_group_set_target (group, target1);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
assert_blocking (target1, group, 1);
assert_blocking (target1, group, 3);
assert_blocking (target1, group, 15);
/* Assert that blocking transfers across changing the target */
g_signal_group_block (group);
g_signal_group_block (group);
/* Set a new target when the current target is non-NULL */
target2 = g_object_new (signal_target_get_type (), NULL);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target1);
g_object_unref (readback);
g_signal_group_set_target (group, target2);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target2);
g_object_unref (readback);
assert_signals (target2, group, FALSE);
g_signal_group_unblock (group);
assert_signals (target2, group, FALSE);
g_signal_group_unblock (group);
assert_signals (target2, group, TRUE);
g_assert_finalize_object (target2);
g_assert_finalize_object (target1);
g_assert_finalize_object (group);
}
static void
test_signal_group_weak_ref_target (void)
{
SignalTarget *target = g_object_new (signal_target_get_type (), NULL);
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
SignalTarget *readback;
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (target);
g_assert_null (g_signal_group_dup_target (group));
g_assert_finalize_object (group);
}
static void
test_signal_group_connect_object (void)
{
GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
SignalTarget *target = g_object_new (signal_target_get_type (), NULL);
GSignalGroup *group = g_signal_group_new (signal_target_get_type ());
SignalTarget *readback;
/* We already do basic connect_object() tests in connect_signals(),
* this is only needed to test the specifics of connect_object()
*/
g_signal_group_connect_object (group,
"the-signal",
G_CALLBACK (connect_object_cb),
object,
0);
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (object);
/* This would cause a warning if the SignalGroup did not
* have a weakref on the object as it would try to connect again
*/
g_signal_group_set_target (group, NULL);
g_assert_null (g_signal_group_dup_target (group));
g_signal_group_set_target (group, target);
readback = g_signal_group_dup_target (group);
g_assert_true (readback == target);
g_object_unref (readback);
g_assert_finalize_object (group);
g_assert_finalize_object (target);
}
static void
test_signal_group_signal_parsing (void)
{
g_test_trap_subprocess ("/GObject/SignalGroup/signal-parsing/subprocess", 0,
G_TEST_SUBPROCESS_INHERIT_STDERR);
g_test_trap_assert_passed ();
g_test_trap_assert_stderr ("");
}
static void
test_signal_group_signal_parsing_subprocess (void)
{
GSignalGroup *group;
/* Check that the class has not been created and with it the
* signals registered. This will cause g_signal_parse_name()
* to fail unless GSignalGroup calls g_type_class_ref().
*/
g_assert_null (g_type_class_peek (signal_target_get_type ()));
group = g_signal_group_new (signal_target_get_type ());
g_signal_group_connect (group,
"the-signal",
G_CALLBACK (connect_before_cb),
NULL);
g_assert_finalize_object (group);
}
static void
test_signal_group_properties (void)
{
GSignalGroup *group;
SignalTarget *target, *other;
GType gtype;
group = g_signal_group_new (signal_target_get_type ());
g_object_get (group,
"target", &target,
"target-type", &gtype,
NULL);
g_assert_cmpint (gtype, ==, signal_target_get_type ());
g_assert_null (target);
target = g_object_new (signal_target_get_type (), NULL);
g_object_set (group, "target", target, NULL);
g_object_get (group, "target", &other, NULL);
g_assert_true (target == other);
g_object_unref (other);
g_assert_finalize_object (target);
g_assert_null (g_signal_group_dup_target (group));
g_assert_finalize_object (group);
}
G_DECLARE_INTERFACE (SignalThing, signal_thing, SIGNAL, THING, GObject)
struct _SignalThingInterface
{
GTypeInterface iface;
void (*changed) (SignalThing *thing);
};
G_DEFINE_INTERFACE (SignalThing, signal_thing, G_TYPE_OBJECT)
static guint signal_thing_changed;
static void
signal_thing_default_init (SignalThingInterface *iface)
{
signal_thing_changed =
g_signal_new ("changed",
G_TYPE_FROM_INTERFACE (iface),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SignalThingInterface, changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
G_GNUC_NORETURN static void
thing_changed_cb (SignalThing *thing,
gpointer user_data G_GNUC_UNUSED)
{
g_assert_not_reached ();
}
static void
test_signal_group_interface (void)
{
GSignalGroup *group;
group = g_signal_group_new (signal_thing_get_type ());
g_signal_group_connect (group,
"changed",
G_CALLBACK (thing_changed_cb),
NULL);
g_assert_finalize_object (group);
}
gint
main (gint argc,
gchar *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/GObject/SignalGroup/invalid", test_signal_group_invalid);
g_test_add_func ("/GObject/SignalGroup/simple", test_signal_group_simple);
g_test_add_func ("/GObject/SignalGroup/changing-target", test_signal_group_changing_target);
g_test_add_func ("/GObject/SignalGroup/blocking", test_signal_group_blocking);
g_test_add_func ("/GObject/SignalGroup/weak-ref-target", test_signal_group_weak_ref_target);
g_test_add_func ("/GObject/SignalGroup/connect-object", test_signal_group_connect_object);
g_test_add_func ("/GObject/SignalGroup/signal-parsing", test_signal_group_signal_parsing);
g_test_add_func ("/GObject/SignalGroup/signal-parsing/subprocess", test_signal_group_signal_parsing_subprocess);
g_test_add_func ("/GObject/SignalGroup/properties", test_signal_group_properties);
g_test_add_func ("/GObject/SignalGroup/interface", test_signal_group_interface);
return g_test_run ();
}

1828
gobject/tests/signals.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
#!/usr/bin/env python
# coding=utf-8
# Copyright (c) 2015 Remko Tronçon (https://el-tramo.be)
# Copied from https://github.com/remko/pycotap/
#
# Released under the MIT license
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import unittest
import sys
import base64
from io import StringIO
# Log modes
class LogMode(object):
LogToError, LogToDiagnostics, LogToYAML, LogToAttachment = range(4)
class TAPTestResult(unittest.TestResult):
def __init__(self, output_stream, error_stream, message_log, test_output_log):
super(TAPTestResult, self).__init__(self, output_stream)
self.output_stream = output_stream
self.error_stream = error_stream
self.orig_stdout = None
self.orig_stderr = None
self.message = None
self.test_output = None
self.message_log = message_log
self.test_output_log = test_output_log
self.output_stream.write("TAP version 13\n")
self._set_streams()
def printErrors(self):
self.print_raw("1..%d\n" % self.testsRun)
self._reset_streams()
def _set_streams(self):
self.orig_stdout = sys.stdout
self.orig_stderr = sys.stderr
if self.message_log == LogMode.LogToError:
self.message = self.error_stream
else:
self.message = StringIO()
if self.test_output_log == LogMode.LogToError:
self.test_output = self.error_stream
else:
self.test_output = StringIO()
if self.message_log == self.test_output_log:
self.test_output = self.message
sys.stdout = sys.stderr = self.test_output
def _reset_streams(self):
sys.stdout = self.orig_stdout
sys.stderr = self.orig_stderr
def print_raw(self, text):
self.output_stream.write(text)
self.output_stream.flush()
def print_result(self, result, test, directive=None):
self.output_stream.write("%s %d %s" % (result, self.testsRun, test.id()))
if directive:
self.output_stream.write(" # " + directive)
self.output_stream.write("\n")
self.output_stream.flush()
def ok(self, test, directive=None):
self.print_result("ok", test, directive)
def not_ok(self, test):
self.print_result("not ok", test)
def startTest(self, test):
super(TAPTestResult, self).startTest(test)
def stopTest(self, test):
super(TAPTestResult, self).stopTest(test)
if self.message_log == self.test_output_log:
logs = [(self.message_log, self.message, "output")]
else:
logs = [
(self.test_output_log, self.test_output, "test_output"),
(self.message_log, self.message, "message"),
]
for log_mode, log, log_name in logs:
if log_mode != LogMode.LogToError:
output = log.getvalue()
if len(output):
if log_mode == LogMode.LogToYAML:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ": |\n")
self.print_raw(
" " + output.rstrip().replace("\n", "\n ") + "\n"
)
self.print_raw(" ...\n")
elif log_mode == LogMode.LogToAttachment:
self.print_raw(" ---\n")
self.print_raw(" " + log_name + ":\n")
self.print_raw(" File-Name: " + log_name + ".txt\n")
self.print_raw(" File-Type: text/plain\n")
self.print_raw(
" File-Content: " + base64.b64encode(output) + "\n"
)
self.print_raw(" ...\n")
else:
self.print_raw(
"# " + output.rstrip().replace("\n", "\n# ") + "\n"
)
# Truncate doesn't change the current stream position.
# Seek to the beginning to avoid extensions on subsequent writes.
log.seek(0)
log.truncate(0)
def addSuccess(self, test):
super(TAPTestResult, self).addSuccess(test)
self.ok(test)
def addError(self, test, err):
super(TAPTestResult, self).addError(test, err)
self.message.write(self.errors[-1][1] + "\n")
self.not_ok(test)
def addFailure(self, test, err):
super(TAPTestResult, self).addFailure(test, err)
self.message.write(self.failures[-1][1] + "\n")
self.not_ok(test)
def addSkip(self, test, reason):
super(TAPTestResult, self).addSkip(test, reason)
self.ok(test, "SKIP " + reason)
def addExpectedFailure(self, test, err):
super(TAPTestResult, self).addExpectedFailure(test, err)
self.ok(test)
def addUnexpectedSuccess(self, test):
super(TAPTestResult, self).addUnexpectedSuccess(test)
self.message.write("Unexpected success" + "\n")
self.not_ok(test)
class TAPTestRunner(object):
def __init__(
self,
message_log=LogMode.LogToYAML,
test_output_log=LogMode.LogToDiagnostics,
output_stream=sys.stdout,
error_stream=sys.stderr,
):
self.output_stream = output_stream
self.error_stream = error_stream
self.message_log = message_log
self.test_output_log = test_output_log
def run(self, test):
result = TAPTestResult(
self.output_stream,
self.error_stream,
self.message_log,
self.test_output_log,
)
test(result)
result.printErrors()
return result

105
gobject/tests/testcommon.h Normal file
View file

@ -0,0 +1,105 @@
/* GObject - GLib Type, Object, Parameter and Signal Library
* Copyright (C) 2003 Red Hat, Inc.
*
* 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.1 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/>.
*/
#ifndef __TEST_COMMON_H__
#define __TEST_COMMON_H__
G_BEGIN_DECLS
#define DEFINE_TYPE_FULL(name, prefix, \
class_init, base_init, instance_init, \
parent_type, interface_decl) \
GType \
prefix ## _get_type (void) \
{ \
static GType object_type = 0; \
\
if (!object_type) \
{ \
static const GTypeInfo object_info = \
{ \
sizeof (name ## Class), \
(GBaseInitFunc) base_init, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) class_init, \
(GClassFinalizeFunc) NULL, \
NULL, /* class_data */ \
sizeof (name), \
0, /* n_prelocs */ \
(GInstanceInitFunc) instance_init, \
(const GTypeValueTable *) NULL, \
}; \
\
object_type = g_type_register_static (parent_type, \
# name, \
&object_info, 0); \
interface_decl \
} \
\
return object_type; \
}
#define DEFINE_TYPE(name, prefix, \
class_init, base_init, instance_init, \
parent_type) \
DEFINE_TYPE_FULL(name, prefix, class_init, base_init, \
instance_init, parent_type, {})
#define DEFINE_IFACE(name, prefix, base_init, dflt_init) \
GType \
prefix ## _get_type (void) \
{ \
static GType iface_type = 0; \
\
if (!iface_type) \
{ \
static const GTypeInfo iface_info = \
{ \
sizeof (name ## Class), \
(GBaseInitFunc) base_init, \
(GBaseFinalizeFunc) NULL, \
(GClassInitFunc) dflt_init, \
(GClassFinalizeFunc) NULL, \
(gconstpointer) NULL, \
(guint16) 0, \
(guint16) 0, \
(GInstanceInitFunc) NULL, \
(const GTypeValueTable*) NULL, \
}; \
\
iface_type = g_type_register_static (G_TYPE_INTERFACE, \
# name, \
&iface_info, 0); \
} \
return iface_type; \
}
#define INTERFACE_FULL(type, init_func, iface_type) \
{ \
static GInterfaceInfo const iface = \
{ \
(GInterfaceInitFunc) init_func, NULL, NULL \
}; \
\
g_type_add_interface_static (type, iface_type, &iface); \
}
#define INTERFACE(init_func, iface_type) \
INTERFACE_FULL(object_type, init_func, iface_type)
G_END_DECLS
#endif /* __TEST_COMMON_H__ */

71
gobject/tests/testing.c Normal file
View file

@ -0,0 +1,71 @@
/* GLib testing framework examples and tests
*
* Copyright © 2019 Endless Mobile, Inc.
*
* 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.1 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/>.
*
* Author: Philip Withnall <withnall@endlessm.com>
*/
#include "config.h"
/* We want to distinguish between messages originating from libglib
* and messages originating from this program.
*/
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "testing"
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
#include <stdlib.h>
static void
test_assert_finalize_object_subprocess_bad (void)
{
GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
g_object_ref (obj);
/* This should emit an assertion failure. */
g_assert_finalize_object (obj);
g_object_unref (obj);
exit (0);
}
static void
test_assert_finalize_object (void)
{
GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
g_assert_finalize_object (obj);
g_test_trap_subprocess ("/assert/finalize_object/subprocess/bad", 0, 0);
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*g_assert_finalize_object:*'weak_pointer' should be NULL*");
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/assert/finalize_object", test_assert_finalize_object);
g_test_add_func ("/assert/finalize_object/subprocess/bad",
test_assert_finalize_object_subprocess_bad);
return g_test_run ();
}

525
gobject/tests/threadtests.c Normal file
View file

@ -0,0 +1,525 @@
/* GLib testing framework examples and tests
* Copyright (C) 2008 Imendio AB
* Authors: Tim Janik
*
* This work is provided "as is"; redistribution and modification
* in whole or in part, in any medium, physical or electronic is
* permitted without restriction.
*
* This work 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.
*
* In no event shall the authors or contributors be liable for any
* direct, indirect, incidental, special, exemplary, or consequential
* damages (including, but not limited to, procurement of substitute
* goods or services; loss of use, data, or profits; or business
* interruption) however caused and on any theory of liability, whether
* in contract, strict liability, or tort (including negligence or
* otherwise) arising in any way out of the use of this software, even
* if advised of the possibility of such damage.
*/
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#endif
#include <glib.h>
#include <glib-object.h>
static int mtsafe_call_counter = 0; /* multi thread safe call counter, must be accessed atomically */
static int unsafe_call_counter = 0; /* single-threaded call counter */
static GCond sync_cond;
static GMutex sync_mutex;
#define NUM_COUNTER_INCREMENTS 100000
static void
call_counter_init (gpointer tclass)
{
int i;
for (i = 0; i < NUM_COUNTER_INCREMENTS; i++)
{
int saved_unsafe_call_counter = unsafe_call_counter;
g_atomic_int_add (&mtsafe_call_counter, 1); /* real call count update */
g_thread_yield(); /* let concurrent threads corrupt the unsafe_call_counter state */
unsafe_call_counter = 1 + saved_unsafe_call_counter; /* non-atomic counter update */
}
}
static void interface_per_class_init (void) { call_counter_init (NULL); }
/* define 3 test interfaces */
typedef GTypeInterface MyFace0Interface;
static GType my_face0_get_type (void);
G_DEFINE_INTERFACE (MyFace0, my_face0, G_TYPE_OBJECT)
static void my_face0_default_init (MyFace0Interface *iface) { call_counter_init (iface); }
typedef GTypeInterface MyFace1Interface;
static GType my_face1_get_type (void);
G_DEFINE_INTERFACE (MyFace1, my_face1, G_TYPE_OBJECT)
static void my_face1_default_init (MyFace1Interface *iface) { call_counter_init (iface); }
/* define 3 test objects, adding interfaces 0 & 1, and adding interface 2 after class initialization */
typedef GObject MyTester0;
typedef GObjectClass MyTester0Class;
static GType my_tester0_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyTester0, my_tester0, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
static void my_tester0_init (MyTester0*t) {}
static void my_tester0_class_init (MyTester0Class*c) { call_counter_init (c); }
typedef GObject MyTester1;
typedef GObjectClass MyTester1Class;
/* Disabled for now (see https://bugzilla.gnome.org/show_bug.cgi?id=687659) */
#if 0
typedef GTypeInterface MyFace2Interface;
static GType my_face2_get_type (void);
G_DEFINE_INTERFACE (MyFace2, my_face2, G_TYPE_OBJECT)
static void my_face2_default_init (MyFace2Interface *iface) { call_counter_init (iface); }
static GType my_tester1_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyTester1, my_tester1, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
static void my_tester1_init (MyTester1*t) {}
static void my_tester1_class_init (MyTester1Class*c) { call_counter_init (c); }
typedef GObject MyTester2;
typedef GObjectClass MyTester2Class;
static GType my_tester2_get_type (void);
G_DEFINE_TYPE_WITH_CODE (MyTester2, my_tester2, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
static void my_tester2_init (MyTester2*t) {}
static void my_tester2_class_init (MyTester2Class*c) { call_counter_init (c); }
static gpointer
tester_init_thread (gpointer data)
{
const GInterfaceInfo face2_interface_info = { (GInterfaceInitFunc) interface_per_class_init, NULL, NULL };
gpointer klass;
/* first, synchronize with other threads,
* then run interface and class initializers,
* using unsafe_call_counter concurrently
*/
g_mutex_lock (&sync_mutex);
g_mutex_unlock (&sync_mutex);
/* test default interface initialization for face0 */
g_type_default_interface_unref (g_type_default_interface_ref (my_face0_get_type()));
/* test class initialization, face0 per-class initializer, face1 default and per-class initializer */
klass = g_type_class_ref ((GType) data);
/* test face2 default and per-class initializer, after class_init */
g_type_add_interface_static (G_TYPE_FROM_CLASS (klass), my_face2_get_type(), &face2_interface_info);
/* cleanups */
g_type_class_unref (klass);
return NULL;
}
static void
test_threaded_class_init (void)
{
GThread *t1, *t2, *t3;
/* pause newly created threads */
g_mutex_lock (&sync_mutex);
/* create threads */
t1 = g_thread_create (tester_init_thread, (gpointer) my_tester0_get_type(), TRUE, NULL);
t2 = g_thread_create (tester_init_thread, (gpointer) my_tester1_get_type(), TRUE, NULL);
t3 = g_thread_create (tester_init_thread, (gpointer) my_tester2_get_type(), TRUE, NULL);
/* execute threads */
g_mutex_unlock (&sync_mutex);
while (g_atomic_int_get (&mtsafe_call_counter) < (3 + 3 + 3 * 3) * NUM_COUNTER_INCREMENTS)
{
if (g_test_verbose())
g_printerr ("Initializers counted: %u\n", g_atomic_int_get (&mtsafe_call_counter));
g_usleep (50 * 1000); /* wait for threads to complete */
}
if (g_test_verbose())
g_printerr ("Total initializers: %u\n", g_atomic_int_get (&mtsafe_call_counter));
/* ensure non-corrupted counter updates */
g_assert_cmpint (g_atomic_int_get (&mtsafe_call_counter), ==, unsafe_call_counter);
g_thread_join (t1);
g_thread_join (t2);
g_thread_join (t3);
}
#endif
typedef struct {
GObject parent;
char *name;
} PropTester;
typedef GObjectClass PropTesterClass;
static GType prop_tester_get_type (void);
G_DEFINE_TYPE (PropTester, prop_tester, G_TYPE_OBJECT)
#define PROP_NAME 1
static void
prop_tester_init (PropTester* t)
{
if (t->name == NULL)
{ } /* needs unit test framework initialization: g_test_bug ("race initializing properties"); */
}
static void
prop_tester_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{}
static void
prop_tester_class_init (PropTesterClass *c)
{
int i;
GParamSpec *param;
GObjectClass *gobject_class = G_OBJECT_CLASS (c);
gobject_class->set_property = prop_tester_set_property; /* silence GObject checks */
g_mutex_lock (&sync_mutex);
g_cond_signal (&sync_cond);
g_mutex_unlock (&sync_mutex);
for (i = 0; i < 100; i++) /* wait a bit. */
g_thread_yield();
call_counter_init (c);
param = g_param_spec_string ("name", "name_i18n",
"yet-more-wasteful-i18n",
NULL,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB |
G_PARAM_STATIC_NICK);
g_object_class_install_property (gobject_class, PROP_NAME, param);
}
static gpointer
object_create (gpointer data)
{
GObject *obj = g_object_new (prop_tester_get_type(), "name", "fish", NULL);
g_object_unref (obj);
return NULL;
}
static void
test_threaded_object_init (void)
{
GThread *creator;
g_mutex_lock (&sync_mutex);
creator = g_thread_create (object_create, NULL, TRUE, NULL);
/* really provoke the race */
g_cond_wait (&sync_cond, &sync_mutex);
object_create (NULL);
g_mutex_unlock (&sync_mutex);
g_thread_join (creator);
}
typedef struct {
MyTester0 *strong;
guint unref_delay;
} UnrefInThreadData;
static gpointer
unref_in_thread (gpointer p)
{
UnrefInThreadData *data = p;
g_usleep (data->unref_delay);
g_object_unref (data->strong);
return NULL;
}
/* undefine to see this test fail without GWeakRef */
#define HAVE_G_WEAK_REF
#define SLEEP_MIN_USEC 1
#define SLEEP_MAX_USEC 10
static void
test_threaded_weak_ref (void)
{
guint i;
guint get_wins = 0, unref_wins = 0;
guint n;
if (g_test_thorough ())
n = NUM_COUNTER_INCREMENTS;
else
n = NUM_COUNTER_INCREMENTS / 20;
#ifdef G_OS_WIN32
/* On Windows usleep has millisecond resolution and gets rounded up
* leading to the test running for a long time. */
n /= 10;
#endif
for (i = 0; i < n; i++)
{
UnrefInThreadData data;
#ifdef HAVE_G_WEAK_REF
/* GWeakRef<MyTester0> in C++ terms */
GWeakRef weak;
#else
gpointer weak;
#endif
MyTester0 *strengthened;
guint get_delay;
GThread *thread;
GError *error = NULL;
if (g_test_verbose () && (i % (n/20)) == 0)
g_printerr ("%u%%\n", ((i * 100) / n));
/* Have an object and a weak ref to it */
data.strong = g_object_new (my_tester0_get_type (), NULL);
#ifdef HAVE_G_WEAK_REF
g_weak_ref_init (&weak, data.strong);
#else
weak = data.strong;
g_object_add_weak_pointer ((GObject *) weak, &weak);
#endif
/* Delay for a random time on each side of the race, to perturb the
* timing. Ideally, we want each side to win half the races; on
* smcv's laptop, these timings are about right.
*/
data.unref_delay = g_random_int_range (SLEEP_MIN_USEC / 2, SLEEP_MAX_USEC / 2);
get_delay = g_random_int_range (SLEEP_MIN_USEC, SLEEP_MAX_USEC);
/* One half of the race is to unref the shared object */
thread = g_thread_create (unref_in_thread, &data, TRUE, &error);
g_assert_no_error (error);
/* The other half of the race is to get the object from the "global
* singleton"
*/
g_usleep (get_delay);
#ifdef HAVE_G_WEAK_REF
strengthened = g_weak_ref_get (&weak);
#else
/* Spot the unsafe pointer access! In GDBusConnection this is rather
* better-hidden, but ends up with essentially the same thing, albeit
* cleared in dispose() rather than by a traditional weak pointer
*/
strengthened = weak;
if (strengthened != NULL)
g_object_ref (strengthened);
#endif
if (strengthened != NULL)
g_assert (G_IS_OBJECT (strengthened));
/* Wait for the thread to run */
g_thread_join (thread);
if (strengthened != NULL)
{
get_wins++;
g_assert (G_IS_OBJECT (strengthened));
g_object_unref (strengthened);
}
else
{
unref_wins++;
}
#ifdef HAVE_G_WEAK_REF
g_weak_ref_clear (&weak);
#else
if (weak != NULL)
g_object_remove_weak_pointer (weak, &weak);
#endif
}
if (g_test_verbose ())
g_printerr ("Race won by get %u times, unref %u times\n",
get_wins, unref_wins);
}
typedef struct
{
GObject *object;
GWeakRef *weak;
gint started; /* (atomic) */
gint finished; /* (atomic) */
gint disposing; /* (atomic) */
} ThreadedWeakRefData;
static void
on_weak_ref_disposed (gpointer data,
GObject *gobj)
{
ThreadedWeakRefData *thread_data = data;
/* Wait until the thread has started */
while (!g_atomic_int_get (&thread_data->started))
continue;
g_atomic_int_set (&thread_data->disposing, 1);
/* Wait for the thread to act, so that the object is still valid */
while (!g_atomic_int_get (&thread_data->finished))
continue;
g_atomic_int_set (&thread_data->disposing, 0);
}
static gpointer
on_other_thread_weak_ref (gpointer user_data)
{
ThreadedWeakRefData *thread_data = user_data;
GObject *object = thread_data->object;
g_atomic_int_set (&thread_data->started, 1);
/* Ensure we've started disposal */
while (!g_atomic_int_get (&thread_data->disposing))
continue;
g_object_ref (object);
g_weak_ref_set (thread_data->weak, object);
g_object_unref (object);
g_assert_cmpint (thread_data->disposing, ==, 1);
g_atomic_int_set (&thread_data->finished, 1);
return NULL;
}
static void
test_threaded_weak_ref_finalization (void)
{
GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
ThreadedWeakRefData thread_data = {
.object = obj, .weak = &weak, .started = 0, .finished = 0
};
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
g_test_summary ("Test that a weak ref added by another thread during dispose "
"of a GObject is cleared during finalisation. "
"Use on_weak_ref_disposed() to synchronize the other thread "
"with the dispose vfunc.");
g_weak_ref_init (&weak, NULL);
g_object_weak_ref (obj, on_weak_ref_disposed, &thread_data);
g_assert_cmpint (obj->ref_count, ==, 1);
g_thread_unref (g_thread_new ("on_other_thread",
on_other_thread_weak_ref,
&thread_data));
g_object_unref (obj);
/* This is what this test is about: at this point the weak reference
* should have been unset (and not point to a dead object either). */
g_assert_null (g_weak_ref_get (&weak));
}
typedef struct
{
GObject *object;
int done; /* (atomic) */
int toggles; /* (atomic) */
} ToggleNotifyThreadData;
static gpointer
on_reffer_thread (gpointer user_data)
{
ToggleNotifyThreadData *thread_data = user_data;
while (!g_atomic_int_get (&thread_data->done))
{
g_object_ref (thread_data->object);
g_object_unref (thread_data->object);
}
return NULL;
}
static void
on_toggle_notify (gpointer data,
GObject *object,
gboolean is_last_ref)
{
/* Anything could be put here, but we don't care for this test.
* Actually having this empty made the bug to happen more frequently (being
* timing related).
*/
}
static gpointer
on_toggler_thread (gpointer user_data)
{
ToggleNotifyThreadData *thread_data = user_data;
while (!g_atomic_int_get (&thread_data->done))
{
g_object_ref (thread_data->object);
g_object_remove_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
g_object_add_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
g_object_unref (thread_data->object);
g_atomic_int_add (&thread_data->toggles, 1);
}
return NULL;
}
static void
test_threaded_toggle_notify (void)
{
GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
ToggleNotifyThreadData data = { object, FALSE, 0 };
GThread *threads[3];
gsize i;
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2394");
g_test_summary ("Test that toggle reference notifications can be changed "
"safely from another (the main) thread without causing the "
"notifying thread to abort");
g_object_add_toggle_ref (object, on_toggle_notify, &data);
g_object_unref (object);
g_assert_cmpint (object->ref_count, ==, 1);
threads[0] = g_thread_new ("on_reffer_thread", on_reffer_thread, &data);
threads[1] = g_thread_new ("on_another_reffer_thread", on_reffer_thread, &data);
threads[2] = g_thread_new ("on_main_toggler_thread", on_toggler_thread, &data);
/* We need to wait here for the threads to run for a bit in order to make the
* race to happen, so we wait for an high number of toggle changes to be met
* so that we can be consistent on each platform.
*/
while (g_atomic_int_get (&data.toggles) < 1000000)
;
g_atomic_int_set (&data.done, TRUE);
for (i = 0; i < G_N_ELEMENTS (threads); i++)
g_thread_join (threads[i]);
g_assert_cmpint (object->ref_count, ==, 1);
g_clear_object (&object);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
/* g_test_add_func ("/GObject/threaded-class-init", test_threaded_class_init); */
g_test_add_func ("/GObject/threaded-object-init", test_threaded_object_init);
g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref);
g_test_add_func ("/GObject/threaded-weak-ref/on-finalization",
test_threaded_weak_ref_finalization);
g_test_add_func ("/GObject/threaded-toggle-notify",
test_threaded_toggle_notify);
return g_test_run();
}

View file

@ -0,0 +1,88 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2021 Emmanuele Bassi
#include <glib-object.h>
#define TEST_TYPE_FINAL (test_final_get_type())
G_DECLARE_FINAL_TYPE (TestFinal, test_final, TEST, FINAL, GObject)
struct _TestFinal
{
GObject parent_instance;
};
struct _TestFinalClass
{
GObjectClass parent_class;
};
G_DEFINE_FINAL_TYPE (TestFinal, test_final, G_TYPE_OBJECT)
static void
test_final_class_init (TestFinalClass *klass)
{
}
static void
test_final_init (TestFinal *self)
{
}
#define TEST_TYPE_FINAL2 (test_final2_get_type())
G_DECLARE_FINAL_TYPE (TestFinal2, test_final2, TEST, FINAL2, TestFinal)
struct _TestFinal2
{
TestFinal parent_instance;
};
struct _TestFinal2Class
{
TestFinalClass parent_class;
};
G_DEFINE_TYPE (TestFinal2, test_final2, TEST_TYPE_FINAL)
static void
test_final2_class_init (TestFinal2Class *klass)
{
}
static void
test_final2_init (TestFinal2 *self)
{
}
/* test_type_flags_final: Check that trying to derive from a final class
* will result in a warning from the type system
*/
static void
test_type_flags_final (void)
{
GType final2_type;
/* This is the message we print out when registering the type */
g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING,
"*cannot derive*");
/* This is the message when we fail to return from the GOnce init
* block within the test_final2_get_type() function
*/
g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL,
"*g_once_init_leave: assertion*");
final2_type = TEST_TYPE_FINAL2;
g_assert_true (final2_type == G_TYPE_INVALID);
g_test_assert_expected_messages ();
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/type/flags/final", test_type_flags_final);
return g_test_run ();
}

215
gobject/tests/type.c Normal file
View file

@ -0,0 +1,215 @@
#include <glib-object.h>
static void
test_registration_serial (void)
{
gint serial1, serial2, serial3;
serial1 = g_type_get_type_registration_serial ();
g_pointer_type_register_static ("my+pointer");
serial2 = g_type_get_type_registration_serial ();
g_assert (serial1 != serial2);
serial3 = g_type_get_type_registration_serial ();
g_assert (serial2 == serial3);
}
typedef struct {
GTypeInterface g_iface;
} BarInterface;
GType bar_get_type (void);
G_DEFINE_INTERFACE (Bar, bar, G_TYPE_OBJECT)
static void
bar_default_init (BarInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} FooInterface;
GType foo_get_type (void);
G_DEFINE_INTERFACE_WITH_CODE (Foo, foo, G_TYPE_OBJECT,
g_type_interface_add_prerequisite (g_define_type_id, bar_get_type ()))
static void
foo_default_init (FooInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BaaInterface;
GType baa_get_type (void);
G_DEFINE_INTERFACE (Baa, baa, G_TYPE_INVALID)
static void
baa_default_init (BaaInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BooInterface;
GType boo_get_type (void);
G_DEFINE_INTERFACE_WITH_CODE (Boo, boo, G_TYPE_INVALID,
g_type_interface_add_prerequisite (g_define_type_id, baa_get_type ()))
static void
boo_default_init (BooInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BibiInterface;
GType bibi_get_type (void);
G_DEFINE_INTERFACE (Bibi, bibi, G_TYPE_INITIALLY_UNOWNED)
static void
bibi_default_init (BibiInterface *iface)
{
}
typedef struct {
GTypeInterface g_iface;
} BozoInterface;
GType bozo_get_type (void);
G_DEFINE_INTERFACE_WITH_CODE (Bozo, bozo, G_TYPE_INVALID,
g_type_interface_add_prerequisite (g_define_type_id, foo_get_type ());
g_type_interface_add_prerequisite (g_define_type_id, bibi_get_type ()))
static void
bozo_default_init (BozoInterface *iface)
{
}
static void
test_interface_prerequisite (void)
{
GType *prereqs;
guint n_prereqs;
gpointer iface;
gpointer parent;
prereqs = g_type_interface_prerequisites (foo_get_type (), &n_prereqs);
g_assert_cmpint (n_prereqs, ==, 2);
g_assert (prereqs[0] == bar_get_type ());
g_assert (prereqs[1] == G_TYPE_OBJECT);
g_assert (g_type_interface_instantiatable_prerequisite (foo_get_type ()) == G_TYPE_OBJECT);
iface = g_type_default_interface_ref (foo_get_type ());
parent = g_type_interface_peek_parent (iface);
g_assert (parent == NULL);
g_type_default_interface_unref (iface);
g_free (prereqs);
g_assert_cmpint (g_type_interface_instantiatable_prerequisite (baa_get_type ()), ==, G_TYPE_INVALID);
g_assert_cmpint (g_type_interface_instantiatable_prerequisite (boo_get_type ()), ==, G_TYPE_INVALID);
g_assert_cmpint (g_type_interface_instantiatable_prerequisite (bozo_get_type ()), ==, G_TYPE_INITIALLY_UNOWNED);
}
typedef struct {
GTypeInterface g_iface;
} BazInterface;
GType baz_get_type (void);
G_DEFINE_INTERFACE (Baz, baz, G_TYPE_OBJECT)
static void
baz_default_init (BazInterface *iface)
{
}
typedef struct {
GObject parent;
} Bazo;
typedef struct {
GObjectClass parent_class;
} BazoClass;
GType bazo_get_type (void);
static void bazo_iface_init (BazInterface *i);
G_DEFINE_TYPE_WITH_CODE (Bazo, bazo, G_TYPE_INITIALLY_UNOWNED,
G_IMPLEMENT_INTERFACE (baz_get_type (),
bazo_iface_init);)
static void
bazo_init (Bazo *b)
{
}
static void
bazo_class_init (BazoClass *c)
{
}
static void
bazo_iface_init (BazInterface *i)
{
}
static gint check_called;
static void
check_func (gpointer check_data,
gpointer g_iface)
{
g_assert (check_data == &check_called);
check_called++;
}
static void
test_interface_check (void)
{
GObject *o;
check_called = 0;
g_type_add_interface_check (&check_called, check_func);
o = g_object_new (bazo_get_type (), NULL);
g_object_unref (o);
g_assert_cmpint (check_called, ==, 1);
g_type_remove_interface_check (&check_called, check_func);
}
static void
test_next_base (void)
{
GType type;
type = g_type_next_base (bazo_get_type (), G_TYPE_OBJECT);
g_assert (type == G_TYPE_INITIALLY_UNOWNED);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/type/registration-serial", test_registration_serial);
g_test_add_func ("/type/interface-prerequisite", test_interface_prerequisite);
g_test_add_func ("/type/interface-check", test_interface_check);
g_test_add_func ("/type/next-base", test_next_base);
return g_test_run ();
}

752
gobject/tests/value.c Normal file
View file

@ -0,0 +1,752 @@
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* 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.1 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/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_30
#include <glib.h>
#include <glib-object.h>
#include "gobject/gvaluecollector.h"
static void
test_enum_transformation (void)
{
GType type;
GValue orig = G_VALUE_INIT;
GValue xform = G_VALUE_INIT;
GEnumValue values[] = { {0,"0","0"}, {1,"1","1"}};
type = g_enum_register_static ("TestEnum", values);
g_value_init (&orig, type);
g_value_set_enum (&orig, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_CHAR);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_char (&xform), ==, 1);
g_assert_cmpint (g_value_get_schar (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_UCHAR);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_uchar (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_INT);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_int (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_UINT);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_uint (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_LONG);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_long (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_ULONG);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_ulong (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_INT64);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_int64 (&xform), ==, 1);
memset (&xform, 0, sizeof (GValue));
g_value_init (&xform, G_TYPE_UINT64);
g_value_transform (&orig, &xform);
g_assert_cmpint (g_value_get_uint64 (&xform), ==, 1);
}
static void
test_gtype_value (void)
{
GType type;
GValue value = G_VALUE_INIT;
GValue copy = G_VALUE_INIT;
g_value_init (&value, G_TYPE_GTYPE);
g_value_set_gtype (&value, G_TYPE_BOXED);
type = g_value_get_gtype (&value);
g_assert_true (type == G_TYPE_BOXED);
g_value_init (&copy, G_TYPE_GTYPE);
g_value_copy (&value, &copy);
type = g_value_get_gtype (&copy);
g_assert_true (type == G_TYPE_BOXED);
}
static gchar *
collect (GValue *value, ...)
{
gchar *error;
va_list var_args;
error = NULL;
va_start (var_args, value);
G_VALUE_COLLECT (value, var_args, 0, &error);
va_end (var_args);
return error;
}
static gchar *
lcopy (GValue *value, ...)
{
gchar *error;
va_list var_args;
error = NULL;
va_start (var_args, value);
G_VALUE_LCOPY (value, var_args, 0, &error);
va_end (var_args);
return error;
}
static void
test_collection (void)
{
GValue value = G_VALUE_INIT;
gchar *error;
g_value_init (&value, G_TYPE_CHAR);
error = collect (&value, 'c');
g_assert_null (error);
g_assert_cmpint (g_value_get_char (&value), ==, 'c');
g_value_unset (&value);
g_value_init (&value, G_TYPE_UCHAR);
error = collect (&value, 129);
g_assert_null (error);
g_assert_cmpint (g_value_get_uchar (&value), ==, 129);
g_value_unset (&value);
g_value_init (&value, G_TYPE_BOOLEAN);
error = collect (&value, TRUE);
g_assert_null (error);
g_assert_cmpint (g_value_get_boolean (&value), ==, TRUE);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
error = collect (&value, G_MAXINT);
g_assert_null (error);
g_assert_cmpint (g_value_get_int (&value), ==, G_MAXINT);
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT);
error = collect (&value, G_MAXUINT);
g_assert_null (error);
g_assert_cmpuint (g_value_get_uint (&value), ==, G_MAXUINT);
g_value_unset (&value);
g_value_init (&value, G_TYPE_LONG);
error = collect (&value, G_MAXLONG);
g_assert_null (error);
g_assert_cmpint (g_value_get_long (&value), ==, G_MAXLONG);
g_value_unset (&value);
g_value_init (&value, G_TYPE_ULONG);
error = collect (&value, G_MAXULONG);
g_assert_null (error);
g_assert_cmpuint (g_value_get_ulong (&value), ==, G_MAXULONG);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT64);
error = collect (&value, G_MAXINT64);
g_assert_null (error);
g_assert_cmpint (g_value_get_int64 (&value), ==, G_MAXINT64);
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT64);
error = collect (&value, G_MAXUINT64);
g_assert_null (error);
g_assert_cmpuint (g_value_get_uint64 (&value), ==, G_MAXUINT64);
g_value_unset (&value);
g_value_init (&value, G_TYPE_FLOAT);
error = collect (&value, G_MAXFLOAT);
g_assert_null (error);
g_assert_cmpfloat (g_value_get_float (&value), ==, G_MAXFLOAT);
g_value_unset (&value);
g_value_init (&value, G_TYPE_DOUBLE);
error = collect (&value, G_MAXDOUBLE);
g_assert_null (error);
g_assert_cmpfloat (g_value_get_double (&value), ==, G_MAXDOUBLE);
g_value_unset (&value);
g_value_init (&value, G_TYPE_STRING);
error = collect (&value, "string ?");
g_assert_null (error);
g_assert_cmpstr (g_value_get_string (&value), ==, "string ?");
g_value_unset (&value);
g_value_init (&value, G_TYPE_GTYPE);
error = collect (&value, G_TYPE_BOXED);
g_assert_null (error);
g_assert_true (g_value_get_gtype (&value) == G_TYPE_BOXED);
g_value_unset (&value);
g_value_init (&value, G_TYPE_VARIANT);
error = collect (&value, g_variant_new_uint32 (42));
g_assert_null (error);
g_assert_true (g_variant_is_of_type (g_value_get_variant (&value),
G_VARIANT_TYPE ("u")));
g_assert_cmpuint (g_variant_get_uint32 (g_value_get_variant (&value)), ==, 42);
g_value_unset (&value);
}
static void
test_copying (void)
{
GValue value = G_VALUE_INIT;
gchar *error;
{
gchar c = 0;
g_value_init (&value, G_TYPE_CHAR);
g_value_set_char (&value, 'c');
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpint (c, ==, 'c');
}
{
guchar c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_UCHAR);
g_value_set_uchar (&value, 129);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpint (c, ==, 129);
}
{
gint c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, G_MAXINT);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpint (c, ==, G_MAXINT);
}
{
guint c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT);
g_value_set_uint (&value, G_MAXUINT);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpuint (c, ==, G_MAXUINT);
}
{
glong c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_LONG);
g_value_set_long (&value, G_MAXLONG);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXLONG);
}
{
gulong c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_ULONG);
g_value_set_ulong (&value, G_MAXULONG);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXULONG);
}
{
gint64 c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT64);
g_value_set_int64 (&value, G_MAXINT64);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXINT64);
}
{
guint64 c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_UINT64);
g_value_set_uint64 (&value, G_MAXUINT64);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXUINT64);
}
{
gfloat c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_FLOAT);
g_value_set_float (&value, G_MAXFLOAT);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXFLOAT);
}
{
gdouble c = 0;
g_value_unset (&value);
g_value_init (&value, G_TYPE_DOUBLE);
g_value_set_double (&value, G_MAXDOUBLE);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert (c == G_MAXDOUBLE);
}
{
gchar *c = NULL;
g_value_unset (&value);
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, "string ?");
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_cmpstr (c, ==, "string ?");
g_free (c);
}
{
GType c = G_TYPE_NONE;
g_value_unset (&value);
g_value_init (&value, G_TYPE_GTYPE);
g_value_set_gtype (&value, G_TYPE_BOXED);
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_true (c == G_TYPE_BOXED);
}
{
GVariant *c = NULL;
g_value_unset (&value);
g_value_init (&value, G_TYPE_VARIANT);
g_value_set_variant (&value, g_variant_new_uint32 (42));
error = lcopy (&value, &c);
g_assert_null (error);
g_assert_nonnull (c);
g_assert (g_variant_is_of_type (c, G_VARIANT_TYPE ("u")));
g_assert_cmpuint (g_variant_get_uint32 (c), ==, 42);
g_variant_unref (c);
g_value_unset (&value);
}
}
static void
test_value_basic (void)
{
GValue value = G_VALUE_INIT;
g_assert_false (G_IS_VALUE (&value));
g_assert_false (G_VALUE_HOLDS_INT (&value));
g_value_unset (&value);
g_assert_false (G_IS_VALUE (&value));
g_assert_false (G_VALUE_HOLDS_INT (&value));
g_value_init (&value, G_TYPE_INT);
g_assert_true (G_IS_VALUE (&value));
g_assert_true (G_VALUE_HOLDS_INT (&value));
g_assert_false (G_VALUE_HOLDS_UINT (&value));
g_assert_cmpint (g_value_get_int (&value), ==, 0);
g_value_set_int (&value, 10);
g_assert_cmpint (g_value_get_int (&value), ==, 10);
g_value_reset (&value);
g_assert_true (G_IS_VALUE (&value));
g_assert_true (G_VALUE_HOLDS_INT (&value));
g_assert_cmpint (g_value_get_int (&value), ==, 0);
g_value_unset (&value);
g_assert_false (G_IS_VALUE (&value));
g_assert_false (G_VALUE_HOLDS_INT (&value));
}
static void
test_value_string (void)
{
const gchar *static1 = "static1";
const gchar *static2 = "static2";
const gchar *storedstr;
const gchar *copystr;
gchar *str1, *str2;
GValue value = G_VALUE_INIT;
GValue copy = G_VALUE_INIT;
g_test_summary ("Test that G_TYPE_STRING GValue copy properly");
/*
* Regular strings (ownership not passed)
*/
/* Create a regular string gvalue and make sure it copies the provided string */
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
/* The string contents should be empty at this point */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == NULL);
g_value_set_string (&value, static1);
/* The contents should be a copy of the same string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static1);
g_assert_cmpstr (storedstr, ==, static1);
/* Check g_value_dup_string() provides a copy */
str1 = g_value_dup_string (&value);
g_assert_true (storedstr != str1);
g_assert_cmpstr (str1, ==, static1);
g_free (str1);
/* Copying a regular string gvalue should copy the contents */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
copystr = g_value_get_string (&copy);
g_assert_true (copystr != storedstr);
g_assert_cmpstr (copystr, ==, static1);
g_value_unset (&copy);
/* Setting a new string should change the contents */
g_value_set_string (&value, static2);
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static2);
/* Setting a static string over that should also change it (test for
* coverage and valgrind) */
g_value_set_static_string (&value, static1);
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static1);
/* Giving a string directly (ownership passed) should replace the content */
str2 = g_strdup (static2);
g_value_take_string (&value, str2);
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, str2);
g_value_unset (&value);
/*
* Regular strings (ownership passed)
*/
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
str1 = g_strdup (static1);
g_value_take_string (&value, str1);
/* The contents should be the string we provided */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == str1);
/* But g_value_dup_string() should provide a copy */
str2 = g_value_dup_string (&value);
g_assert_true (storedstr != str2);
g_assert_cmpstr (str2, ==, static1);
g_free (str2);
/* Copying a regular string gvalue (even with ownership passed) should copy
* the contents */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
copystr = g_value_get_string (&copy);
g_assert_true (copystr != storedstr);
g_assert_cmpstr (copystr, ==, static1);
g_value_unset (&copy);
/* Setting a new regular string should change the contents */
g_value_set_string (&value, static2);
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static2);
g_value_unset (&value);
/*
* Static strings
*/
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
g_value_set_static_string (&value, static1);
/* The contents should be the string we provided */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == static1);
/* But g_value_dup_string() should provide a copy */
str2 = g_value_dup_string (&value);
g_assert_true (storedstr != str2);
g_assert_cmpstr (str2, ==, static1);
g_free (str2);
/* Copying a static string gvalue should *actually* copy the contents */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
copystr = g_value_get_string (&copy);
g_assert_true (copystr != static1);
g_value_unset (&copy);
/* Setting a new string should change the contents */
g_value_set_static_string (&value, static2);
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static1);
g_assert_cmpstr (storedstr, ==, static2);
g_value_unset (&value);
/*
* Interned/Canonical strings
*/
static1 = g_intern_static_string (static1);
g_value_init (&value, G_TYPE_STRING);
g_assert_true (G_VALUE_HOLDS_STRING (&value));
g_value_set_interned_string (&value, static1);
g_assert_true (G_VALUE_IS_INTERNED_STRING (&value));
/* The contents should be the string we provided */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr == static1);
/* But g_value_dup_string() should provide a copy */
str2 = g_value_dup_string (&value);
g_assert_true (storedstr != str2);
g_assert_cmpstr (str2, ==, static1);
g_free (str2);
/* Copying an interned string gvalue should *not* copy the contents
* and should still be an interned string */
g_value_init (&copy, G_TYPE_STRING);
g_value_copy (&value, &copy);
g_assert_true (G_VALUE_IS_INTERNED_STRING (&copy));
copystr = g_value_get_string (&copy);
g_assert_true (copystr == static1);
g_value_unset (&copy);
/* Setting a new interned string should change the contents */
static2 = g_intern_static_string (static2);
g_value_set_interned_string (&value, static2);
g_assert_true (G_VALUE_IS_INTERNED_STRING (&value));
/* The contents should be the interned string */
storedstr = g_value_get_string (&value);
g_assert_cmpstr (storedstr, ==, static2);
/* Setting a new regular string should change the contents */
g_value_set_string (&value, static2);
g_assert_false (G_VALUE_IS_INTERNED_STRING (&value));
/* The contents should be a copy of that *new* string */
storedstr = g_value_get_string (&value);
g_assert_true (storedstr != static2);
g_assert_cmpstr (storedstr, ==, static2);
g_value_unset (&value);
}
static gint
cmpint (gconstpointer a, gconstpointer b)
{
const GValue *aa = a;
const GValue *bb = b;
return g_value_get_int (aa) - g_value_get_int (bb);
}
static void
test_valuearray_basic (void)
{
GValueArray *a;
GValueArray *a2;
GValue v = G_VALUE_INIT;
GValue *p;
guint i;
a = g_value_array_new (20);
g_value_init (&v, G_TYPE_INT);
for (i = 0; i < 100; i++)
{
g_value_set_int (&v, i);
g_value_array_append (a, &v);
}
g_assert_cmpint (a->n_values, ==, 100);
p = g_value_array_get_nth (a, 5);
g_assert_cmpint (g_value_get_int (p), ==, 5);
for (i = 20; i < 100; i+= 5)
g_value_array_remove (a, 100 - i);
for (i = 100; i < 150; i++)
{
g_value_set_int (&v, i);
g_value_array_prepend (a, &v);
}
g_value_array_sort (a, cmpint);
for (i = 0; i < a->n_values - 1; i++)
g_assert_cmpint (g_value_get_int (&a->values[i]), <=, g_value_get_int (&a->values[i+1]));
a2 = g_value_array_copy (a);
for (i = 0; i < a->n_values; i++)
g_assert_cmpint (g_value_get_int (&a->values[i]), ==, g_value_get_int (&a2->values[i]));
g_value_array_free (a);
g_value_array_free (a2);
}
/* We create some dummy objects with this relationship:
*
* GObject TestInterface
* / \ / /
* TestObjectA TestObjectB /
* / \ /
* TestObjectA1 TestObjectA2-------
*
* ie: TestObjectA1 and TestObjectA2 are subclasses of TestObjectA
* and TestObjectB is related to neither. TestObjectA2 and TestObjectB
* implement TestInterface
*/
typedef GTypeInterface TestInterfaceInterface;
static GType test_interface_get_type (void);
G_DEFINE_INTERFACE (TestInterface, test_interface, G_TYPE_OBJECT)
static void test_interface_default_init (TestInterfaceInterface *iface) { }
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;
static void test_object_b_iface_init (TestInterfaceInterface *iface) { }
G_DEFINE_TYPE_WITH_CODE (TestObjectB, test_object_b, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_b_iface_init))
static void test_object_b_class_init (TestObjectBClass *class) { }
static void test_object_b_init (TestObjectB *b) { }
static GType test_object_a1_get_type (void);
typedef GObject TestObjectA1; typedef GObjectClass TestObjectA1Class;
G_DEFINE_TYPE (TestObjectA1, test_object_a1, test_object_a_get_type ())
static void test_object_a1_class_init (TestObjectA1Class *class) { }
static void test_object_a1_init (TestObjectA1 *c) { }
static GType test_object_a2_get_type (void);
typedef GObject TestObjectA2; typedef GObjectClass TestObjectA2Class;
static void test_object_a2_iface_init (TestInterfaceInterface *iface) { }
G_DEFINE_TYPE_WITH_CODE (TestObjectA2, test_object_a2, test_object_a_get_type (),
G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_a2_iface_init))
static void test_object_a2_class_init (TestObjectA2Class *class) { }
static void test_object_a2_init (TestObjectA2 *b) { }
static void
test_value_transform_object (void)
{
GValue src = G_VALUE_INIT;
GValue dest = G_VALUE_INIT;
GObject *object;
guint i, s, d;
GType types[] = {
G_TYPE_OBJECT,
test_interface_get_type (),
test_object_a_get_type (),
test_object_b_get_type (),
test_object_a1_get_type (),
test_object_a2_get_type ()
};
for (i = 0; i < G_N_ELEMENTS (types); i++)
{
if (!G_TYPE_IS_CLASSED (types[i]))
continue;
object = g_object_new (types[i], NULL);
for (s = 0; s < G_N_ELEMENTS (types); s++)
{
if (!G_TYPE_CHECK_INSTANCE_TYPE (object, types[s]))
continue;
g_value_init (&src, types[s]);
g_value_set_object (&src, object);
for (d = 0; d < G_N_ELEMENTS (types); d++)
{
g_test_message ("Next: %s object in GValue of %s to GValue of %s", g_type_name (types[i]), g_type_name (types[s]), g_type_name (types[d]));
g_assert_true (g_value_type_transformable (types[s], types[d]));
g_value_init (&dest, types[d]);
g_assert_true (g_value_transform (&src, &dest));
g_assert_cmpint (g_value_get_object (&dest) != NULL, ==, G_TYPE_CHECK_INSTANCE_TYPE (object, types[d]));
g_value_unset (&dest);
}
g_value_unset (&src);
}
g_object_unref (object);
}
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/value/basic", test_value_basic);
g_test_add_func ("/value/array/basic", test_valuearray_basic);
g_test_add_func ("/value/collection", test_collection);
g_test_add_func ("/value/copying", test_copying);
g_test_add_func ("/value/enum-transformation", test_enum_transformation);
g_test_add_func ("/value/gtype", test_gtype_value);
g_test_add_func ("/value/string", test_value_string);
g_test_add_func ("/value/transform-object", test_value_transform_object);
return g_test_run ();
}