Import Upstream version 2.72.4
This commit is contained in:
commit
4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions
18
gobject/tests/.gitignore
vendored
Normal file
18
gobject/tests/.gitignore
vendored
Normal 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
237
gobject/tests/autoptr.c
Normal 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
1102
gobject/tests/binding.c
Normal file
File diff suppressed because it is too large
Load diff
694
gobject/tests/bindinggroup.c
Normal file
694
gobject/tests/bindinggroup.c
Normal 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
704
gobject/tests/boxed.c
Normal 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 ();
|
||||
}
|
||||
337
gobject/tests/closure-refcount.c
Normal file
337
gobject/tests/closure-refcount.c
Normal 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
|
||||
* don’t terminate until at least 10000 iterations have completed in both
|
||||
* thread1 and thread2. Even though @n_iterations is high, we can’t 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
223
gobject/tests/closure.c
Normal 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
26
gobject/tests/cxx.cpp
Normal 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;
|
||||
}
|
||||
368
gobject/tests/dynamictests.c
Normal file
368
gobject/tests/dynamictests.c
Normal 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
170
gobject/tests/enums.c
Normal 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
179
gobject/tests/flags.c
Normal 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
817
gobject/tests/genmarshal.py
Normal 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())
|
||||
645
gobject/tests/ifaceproperties.c
Normal file
645
gobject/tests/ifaceproperties.c
Normal 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 ();
|
||||
}
|
||||
3
gobject/tests/marshalers.list
Normal file
3
gobject/tests/marshalers.list
Normal 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
161
gobject/tests/meson.build
Normal 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
739
gobject/tests/mkenums.py
Normal 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
150
gobject/tests/object.c
Normal 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
1266
gobject/tests/param.c
Normal file
File diff suppressed because it is too large
Load diff
261
gobject/tests/private.c
Normal file
261
gobject/tests/private.c
Normal 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
658
gobject/tests/properties.c
Normal 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
123
gobject/tests/qdata.c
Normal 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
973
gobject/tests/reference.c
Normal 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 shouldn’t 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 shouldn’t 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() doesn’t 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 ();
|
||||
}
|
||||
299
gobject/tests/signal-handler.c
Normal file
299
gobject/tests/signal-handler.c
Normal 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
650
gobject/tests/signalgroup.c
Normal 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", >ype,
|
||||
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
1828
gobject/tests/signals.c
Normal file
File diff suppressed because it is too large
Load diff
186
gobject/tests/taptestrunner.py
Normal file
186
gobject/tests/taptestrunner.py
Normal 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
105
gobject/tests/testcommon.h
Normal 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
71
gobject/tests/testing.c
Normal 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
525
gobject/tests/threadtests.c
Normal 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();
|
||||
}
|
||||
88
gobject/tests/type-flags.c
Normal file
88
gobject/tests/type-flags.c
Normal 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
215
gobject/tests/type.c
Normal 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
752
gobject/tests/value.c
Normal 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 (©, G_TYPE_GTYPE);
|
||||
g_value_copy (&value, ©);
|
||||
type = g_value_get_gtype (©);
|
||||
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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr != storedstr);
|
||||
g_assert_cmpstr (copystr, ==, static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr != storedstr);
|
||||
g_assert_cmpstr (copystr, ==, static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr != static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 (©, G_TYPE_STRING);
|
||||
g_value_copy (&value, ©);
|
||||
g_assert_true (G_VALUE_IS_INTERNED_STRING (©));
|
||||
copystr = g_value_get_string (©);
|
||||
g_assert_true (copystr == static1);
|
||||
g_value_unset (©);
|
||||
|
||||
/* 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 ();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue