+/* 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);
+
+ /* 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);
+}
+
+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 destroyed;
+static gint value;
+
+static void
+data_destroy (gpointer data)
+{
+ g_assert_cmpint (GPOINTER_TO_INT (data), ==, value);
+
+ destroyed = TRUE;
+}
+
+static void
+test_object_qdata (void)
+{
+ GObject *obj;
+ gpointer v;
+ GQuark quark;
+
+ obj = g_object_new (G_TYPE_OBJECT, NULL);
+
+ value = 1;
+ 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 (destroyed);
+ value = 2;
+ destroyed = FALSE;
+ v = g_object_steal_data (obj, "test");
+ g_assert_cmpint (GPOINTER_TO_INT (v), ==, 2);
+ g_assert (!destroyed);
+
+ value = 1;
+ 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 (destroyed);
+ value = 2;
+ destroyed = FALSE;
+ v = g_object_steal_qdata (obj, quark);
+ g_assert_cmpint (GPOINTER_TO_INT (v), ==, 2);
+ g_assert (!destroyed);
+
+ g_object_set_qdata_full (obj, quark, GINT_TO_POINTER (3), data_destroy);
+ value = 3;
+ destroyed = FALSE;
+ g_object_unref (obj);
+
+ g_assert (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);
+}
+