(root)/
glib-2.79.0/
gobject/
tests/
bindinggroup.c
       1  /* GObject - GLib Type, Object, Parameter and Signal Library
       2   *
       3   * Copyright (C) 2015-2022 Christian Hergert <christian@hergert.me>
       4   * Copyright (C) 2015 Garrett Regier <garrettregier@gmail.com>
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2.1 of the License, or (at your option) any later version.
      12   *
      13   * This library is distributed in the hope that it will be useful,
      14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16   * Lesser General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU Lesser General
      19   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      20   *
      21   * SPDX-License-Identifier: LGPL-2.1-or-later
      22   */
      23  
      24  #include <glib-object.h>
      25  
      26  /* Copied from glib */
      27  typedef struct _BindingSource
      28  {
      29    GObject parent_instance;
      30  
      31    gint foo;
      32    gint bar;
      33    gdouble value;
      34    gboolean toggle;
      35  } BindingSource;
      36  
      37  typedef struct _BindingSourceClass
      38  {
      39    GObjectClass parent_class;
      40  } BindingSourceClass;
      41  
      42  enum
      43  {
      44    PROP_SOURCE_FOO = 1,
      45    PROP_SOURCE_BAR,
      46    PROP_SOURCE_VALUE,
      47    PROP_SOURCE_TOGGLE
      48  };
      49  
      50  static GType binding_source_get_type (void);
      51  G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
      52  
      53  static void
      54  binding_source_set_property (GObject      *gobject,
      55                               guint         prop_id,
      56                               const GValue *value,
      57                               GParamSpec   *pspec)
      58  {
      59    BindingSource *source = (BindingSource *) gobject;
      60  
      61    switch (prop_id)
      62      {
      63      case PROP_SOURCE_FOO:
      64        source->foo = g_value_get_int (value);
      65        break;
      66  
      67      case PROP_SOURCE_BAR:
      68        source->bar = g_value_get_int (value);
      69        break;
      70  
      71      case PROP_SOURCE_VALUE:
      72        source->value = g_value_get_double (value);
      73        break;
      74  
      75      case PROP_SOURCE_TOGGLE:
      76        source->toggle = g_value_get_boolean (value);
      77        break;
      78  
      79      default:
      80        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
      81      }
      82  }
      83  
      84  static void
      85  binding_source_get_property (GObject    *gobject,
      86                               guint       prop_id,
      87                               GValue     *value,
      88                               GParamSpec *pspec)
      89  {
      90    BindingSource *source = (BindingSource *) gobject;
      91  
      92    switch (prop_id)
      93      {
      94      case PROP_SOURCE_FOO:
      95        g_value_set_int (value, source->foo);
      96        break;
      97  
      98      case PROP_SOURCE_BAR:
      99        g_value_set_int (value, source->bar);
     100        break;
     101  
     102      case PROP_SOURCE_VALUE:
     103        g_value_set_double (value, source->value);
     104        break;
     105  
     106      case PROP_SOURCE_TOGGLE:
     107        g_value_set_boolean (value, source->toggle);
     108        break;
     109  
     110      default:
     111        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     112      }
     113  }
     114  
     115  static void
     116  binding_source_class_init (BindingSourceClass *klass)
     117  {
     118    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     119  
     120    gobject_class->set_property = binding_source_set_property;
     121    gobject_class->get_property = binding_source_get_property;
     122  
     123    g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
     124                                     g_param_spec_int ("foo", "Foo", "Foo",
     125                                                       -1, 100,
     126                                                       0,
     127                                                       G_PARAM_READWRITE));
     128    g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
     129                                     g_param_spec_int ("bar", "Bar", "Bar",
     130                                                       -1, 100,
     131                                                       0,
     132                                                       G_PARAM_READWRITE));
     133    g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
     134                                     g_param_spec_double ("value", "Value", "Value",
     135                                                          -100.0, 200.0,
     136                                                          0.0,
     137                                                          G_PARAM_READWRITE));
     138    g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
     139                                     g_param_spec_boolean ("toggle", "Toggle", "Toggle",
     140                                                           FALSE,
     141                                                           G_PARAM_READWRITE));
     142  }
     143  
     144  static void
     145  binding_source_init (BindingSource *self)
     146  {
     147  }
     148  
     149  typedef struct _BindingTarget
     150  {
     151    GObject parent_instance;
     152  
     153    gint bar;
     154    gdouble value;
     155    gboolean toggle;
     156  } BindingTarget;
     157  
     158  typedef struct _BindingTargetClass
     159  {
     160    GObjectClass parent_class;
     161  } BindingTargetClass;
     162  
     163  enum
     164  {
     165    PROP_TARGET_BAR = 1,
     166    PROP_TARGET_VALUE,
     167    PROP_TARGET_TOGGLE
     168  };
     169  
     170  static GType binding_target_get_type (void);
     171  G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
     172  
     173  static void
     174  binding_target_set_property (GObject      *gobject,
     175                               guint         prop_id,
     176                               const GValue *value,
     177                               GParamSpec   *pspec)
     178  {
     179    BindingTarget *target = (BindingTarget *) gobject;
     180  
     181    switch (prop_id)
     182      {
     183      case PROP_TARGET_BAR:
     184        target->bar = g_value_get_int (value);
     185        break;
     186  
     187      case PROP_TARGET_VALUE:
     188        target->value = g_value_get_double (value);
     189        break;
     190  
     191      case PROP_TARGET_TOGGLE:
     192        target->toggle = g_value_get_boolean (value);
     193        break;
     194  
     195      default:
     196        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     197      }
     198  }
     199  
     200  static void
     201  binding_target_get_property (GObject    *gobject,
     202                               guint       prop_id,
     203                               GValue     *value,
     204                               GParamSpec *pspec)
     205  {
     206    BindingTarget *target = (BindingTarget *) gobject;
     207  
     208    switch (prop_id)
     209      {
     210      case PROP_TARGET_BAR:
     211        g_value_set_int (value, target->bar);
     212        break;
     213  
     214      case PROP_TARGET_VALUE:
     215        g_value_set_double (value, target->value);
     216        break;
     217  
     218      case PROP_TARGET_TOGGLE:
     219        g_value_set_boolean (value, target->toggle);
     220        break;
     221  
     222      default:
     223        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     224      }
     225  }
     226  
     227  static void
     228  binding_target_class_init (BindingTargetClass *klass)
     229  {
     230    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     231  
     232    gobject_class->set_property = binding_target_set_property;
     233    gobject_class->get_property = binding_target_get_property;
     234  
     235    g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
     236                                     g_param_spec_int ("bar", "Bar", "Bar",
     237                                                       -1, 100,
     238                                                       0,
     239                                                       G_PARAM_READWRITE));
     240    g_object_class_install_property (gobject_class, PROP_TARGET_VALUE,
     241                                     g_param_spec_double ("value", "Value", "Value",
     242                                                          -100.0, 200.0,
     243                                                          0.0,
     244                                                          G_PARAM_READWRITE));
     245    g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
     246                                     g_param_spec_boolean ("toggle", "Toggle", "Toggle",
     247                                                           FALSE,
     248                                                           G_PARAM_READWRITE));
     249  }
     250  
     251  static void
     252  binding_target_init (BindingTarget *self)
     253  {
     254  }
     255  
     256  static gboolean
     257  celsius_to_fahrenheit (GBinding     *binding,
     258                         const GValue *from_value,
     259                         GValue       *to_value,
     260                         gpointer      user_data G_GNUC_UNUSED)
     261  {
     262    gdouble celsius, fahrenheit;
     263  
     264    g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
     265    g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
     266  
     267    celsius = g_value_get_double (from_value);
     268    fahrenheit = (9 * celsius / 5) + 32.0;
     269  
     270    if (g_test_verbose ())
     271      g_printerr ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
     272  
     273    g_value_set_double (to_value, fahrenheit);
     274  
     275    return TRUE;
     276  }
     277  
     278  static gboolean
     279  fahrenheit_to_celsius (GBinding     *binding,
     280                         const GValue *from_value,
     281                         GValue       *to_value,
     282                         gpointer      user_data G_GNUC_UNUSED)
     283  {
     284    gdouble celsius, fahrenheit;
     285  
     286    g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
     287    g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
     288  
     289    fahrenheit = g_value_get_double (from_value);
     290    celsius = 5 * (fahrenheit - 32.0) / 9;
     291  
     292    if (g_test_verbose ())
     293      g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
     294  
     295    g_value_set_double (to_value, celsius);
     296  
     297    return TRUE;
     298  }
     299  
     300  static void
     301  test_binding_group_invalid (void)
     302  {
     303    GBindingGroup *group = g_binding_group_new ();
     304    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     305    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     306  
     307    /* Invalid Target Property */
     308    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     309                           "*find_property*target_property*!=*NULL*");
     310    g_binding_group_bind (group, "value",
     311                          target, "does-not-exist",
     312                          G_BINDING_DEFAULT);
     313    g_test_assert_expected_messages ();
     314  
     315    g_binding_group_set_source (group, NULL);
     316  
     317    /* Invalid Source Property */
     318    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     319                           "*find_property*source_property*!=*NULL*");
     320    g_binding_group_set_source (group, source);
     321    g_binding_group_bind (group, "does-not-exist",
     322                          target, "value",
     323                          G_BINDING_DEFAULT);
     324    g_test_assert_expected_messages ();
     325  
     326    g_binding_group_set_source (group, NULL);
     327  
     328    /* Invalid Source */
     329    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     330                           "*find_property*->source_property*!=*NULL*");
     331    g_binding_group_bind (group, "does-not-exist",
     332                          target, "value",
     333                          G_BINDING_DEFAULT);
     334    g_binding_group_set_source (group, source);
     335    g_test_assert_expected_messages ();
     336  
     337    g_object_unref (target);
     338    g_object_unref (source);
     339    g_object_unref (group);
     340  }
     341  
     342  static void
     343  test_binding_group_default (void)
     344  {
     345    gsize i, j;
     346    GBindingGroup *group = g_binding_group_new ();
     347    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     348    BindingTarget *targets[5];
     349    BindingSource *readback;
     350  
     351    for (i = 0; i < G_N_ELEMENTS (targets); ++i)
     352      {
     353        targets[i] = g_object_new (binding_target_get_type (), NULL);
     354        g_binding_group_bind (group, "foo",
     355                              targets[i], "bar",
     356                              G_BINDING_DEFAULT);
     357      }
     358  
     359    g_assert_null (g_binding_group_dup_source (group));
     360    g_binding_group_set_source (group, source);
     361    readback = g_binding_group_dup_source (group);
     362    g_assert_true (readback == source);
     363    g_object_unref (readback);
     364  
     365    for (i = 0; i < 2; ++i)
     366      {
     367        g_object_set (source, "foo", 42, NULL);
     368        for (j = 0; j < G_N_ELEMENTS (targets); ++j)
     369          g_assert_cmpint (source->foo, ==, targets[j]->bar);
     370  
     371        g_object_set (targets[0], "bar", 47, NULL);
     372        g_assert_cmpint (source->foo, !=, targets[0]->bar);
     373  
     374        /* Check that we transition the source correctly */
     375        g_binding_group_set_source (group, NULL);
     376        g_assert_null (g_binding_group_dup_source (group));
     377        g_binding_group_set_source (group, source);
     378        readback = g_binding_group_dup_source (group);
     379        g_assert_true (readback == source);
     380        g_object_unref (readback);
     381      }
     382  
     383    g_object_unref (group);
     384  
     385    g_object_set (source, "foo", 0, NULL);
     386    for (i = 0; i < G_N_ELEMENTS (targets); ++i)
     387      g_assert_cmpint (source->foo, !=, targets[i]->bar);
     388  
     389    g_object_unref (source);
     390    for (i = 0; i < G_N_ELEMENTS (targets); ++i)
     391      g_object_unref (targets[i]);
     392  }
     393  
     394  static void
     395  test_binding_group_bidirectional (void)
     396  {
     397    gsize i, j;
     398    GBindingGroup *group = g_binding_group_new ();
     399    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     400    BindingTarget *targets[5];
     401    BindingSource *readback;
     402  
     403    for (i = 0; i < G_N_ELEMENTS (targets); ++i)
     404      {
     405        targets[i] = g_object_new (binding_target_get_type (), NULL);
     406        g_binding_group_bind (group, "value",
     407                              targets[i], "value",
     408                              G_BINDING_BIDIRECTIONAL);
     409      }
     410  
     411    g_assert_null (g_binding_group_dup_source (group));
     412    g_binding_group_set_source (group, source);
     413    readback = g_binding_group_dup_source (group);
     414    g_assert_true (readback == source);
     415    g_object_unref (readback);
     416  
     417    for (i = 0; i < 2; ++i)
     418      {
     419        g_object_set (source, "value", 42.0, NULL);
     420        for (j = 0; j < G_N_ELEMENTS (targets); ++j)
     421          g_assert_cmpfloat (source->value, ==, targets[j]->value);
     422  
     423        g_object_set (targets[0], "value", 47.0, NULL);
     424        g_assert_cmpfloat (source->value, ==, targets[0]->value);
     425  
     426        /* Check that we transition the source correctly */
     427        g_binding_group_set_source (group, NULL);
     428        g_assert_null (g_binding_group_dup_source (group));
     429        g_binding_group_set_source (group, source);
     430        readback = g_binding_group_dup_source (group);
     431        g_assert_true (readback == source);
     432        g_object_unref (readback);
     433      }
     434  
     435    g_object_unref (group);
     436  
     437    g_object_set (targets[0], "value", 0.0, NULL);
     438    g_assert_cmpfloat (source->value, !=, targets[0]->value);
     439  
     440    g_object_unref (source);
     441    for (i = 0; i < G_N_ELEMENTS (targets); ++i)
     442      g_object_unref (targets[i]);
     443  }
     444  
     445  static void
     446  transform_destroy_notify (gpointer data)
     447  {
     448    gboolean *transform_destroy_called = data;
     449  
     450    *transform_destroy_called = TRUE;
     451  }
     452  
     453  static void
     454  test_binding_group_transform (void)
     455  {
     456    gboolean transform_destroy_called = FALSE;
     457    GBindingGroup *group = g_binding_group_new ();
     458    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     459    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     460  
     461    g_binding_group_set_source (group, source);
     462    g_binding_group_bind_full (group, "value",
     463                               target, "value",
     464                               G_BINDING_BIDIRECTIONAL,
     465                               celsius_to_fahrenheit,
     466                               fahrenheit_to_celsius,
     467                               &transform_destroy_called,
     468                               transform_destroy_notify);
     469  
     470    g_object_set (source, "value", 24.0, NULL);
     471    g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
     472  
     473    g_object_set (target, "value", 69.0, NULL);
     474    g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
     475  
     476    /* The GDestroyNotify should only be called when the
     477     * set is freed, not when the various GBindings are freed
     478     */
     479    g_binding_group_set_source (group, NULL);
     480    g_assert_false (transform_destroy_called);
     481  
     482    g_object_unref (group);
     483    g_assert_true (transform_destroy_called);
     484  
     485    g_object_unref (source);
     486    g_object_unref (target);
     487  }
     488  
     489  static void
     490  test_binding_group_transform_closures (void)
     491  {
     492    gboolean transform_destroy_called_1 = FALSE;
     493    gboolean transform_destroy_called_2 = FALSE;
     494    GBindingGroup *group = g_binding_group_new ();
     495    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     496    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     497    GClosure *c2f_closure, *f2c_closure;
     498  
     499    c2f_closure = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit),
     500                                  &transform_destroy_called_1,
     501                                  (GClosureNotify) transform_destroy_notify);
     502    f2c_closure = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius),
     503                                  &transform_destroy_called_2,
     504                                  (GClosureNotify) transform_destroy_notify);
     505  
     506    g_binding_group_set_source (group, source);
     507    g_binding_group_bind_with_closures (group, "value",
     508                                        target, "value",
     509                                        G_BINDING_BIDIRECTIONAL,
     510                                        c2f_closure,
     511                                        f2c_closure);
     512  
     513    g_object_set (source, "value", 24.0, NULL);
     514    g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
     515  
     516    g_object_set (target, "value", 69.0, NULL);
     517    g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
     518  
     519    /* The GClsoureNotify should only be called when the
     520     * set is freed, not when the various GBindings are freed
     521     */
     522    g_binding_group_set_source (group, NULL);
     523    g_assert_false (transform_destroy_called_1);
     524    g_assert_false (transform_destroy_called_2);
     525  
     526    g_object_unref (group);
     527    g_assert_true (transform_destroy_called_1);
     528    g_assert_true (transform_destroy_called_2);
     529  
     530    g_object_unref (source);
     531    g_object_unref (target);
     532  }
     533  
     534  static void
     535  test_binding_group_same_object (void)
     536  {
     537    gsize i;
     538    GBindingGroup *group = g_binding_group_new ();
     539    BindingSource *source = g_object_new (binding_source_get_type (),
     540                                          "foo", 100,
     541                                          "bar", 50,
     542                                          NULL);
     543  
     544    g_binding_group_set_source (group, source);
     545    g_binding_group_bind (group, "foo",
     546                          source, "bar",
     547                          G_BINDING_BIDIRECTIONAL);
     548  
     549    for (i = 0; i < 2; ++i)
     550      {
     551        g_object_set (source, "foo", 10, NULL);
     552        g_assert_cmpint (source->foo, ==, 10);
     553        g_assert_cmpint (source->bar, ==, 10);
     554  
     555        g_object_set (source, "bar", 30, NULL);
     556        g_assert_cmpint (source->foo, ==, 30);
     557        g_assert_cmpint (source->bar, ==, 30);
     558  
     559        /* Check that it is possible both when initially
     560         * adding the binding and when changing the source
     561         */
     562        g_binding_group_set_source (group, NULL);
     563        g_binding_group_set_source (group, source);
     564      }
     565  
     566    g_object_unref (source);
     567    g_object_unref (group);
     568  }
     569  
     570  static void
     571  test_binding_group_weak_ref_source (void)
     572  {
     573    GBindingGroup *group = g_binding_group_new ();
     574    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     575    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     576    BindingSource *readback;
     577  
     578    g_binding_group_set_source (group, source);
     579    g_binding_group_bind (group, "value",
     580                          target, "value",
     581                          G_BINDING_BIDIRECTIONAL);
     582  
     583    g_object_add_weak_pointer (G_OBJECT (source), (gpointer)&source);
     584    readback = g_binding_group_dup_source (group);
     585    g_assert_true (readback == source);
     586    g_object_unref (readback);
     587    g_object_unref (source);
     588    g_assert_null (source);
     589    g_assert_null (g_binding_group_dup_source (group));
     590  
     591    /* Hopefully this would explode if the binding was still alive */
     592    g_object_set (target, "value", 0.0, NULL);
     593  
     594    g_object_unref (target);
     595    g_object_unref (group);
     596  }
     597  
     598  static void
     599  test_binding_group_weak_ref_target (void)
     600  {
     601    GBindingGroup *group = g_binding_group_new ();
     602    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     603    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     604  
     605    g_binding_group_set_source (group, source);
     606    g_binding_group_bind (group, "value",
     607                          target, "value",
     608                          G_BINDING_BIDIRECTIONAL);
     609  
     610    g_object_set (source, "value", 47.0, NULL);
     611    g_assert_cmpfloat (target->value, ==, 47.0);
     612  
     613    g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
     614    g_object_unref (target);
     615    g_assert_null (target);
     616  
     617    /* Hopefully this would explode if the binding was still alive */
     618    g_object_set (source, "value", 0.0, NULL);
     619  
     620    g_object_unref (source);
     621    g_object_unref (group);
     622  }
     623  
     624  static void
     625  test_binding_group_properties (void)
     626  {
     627    GBindingGroup *group = g_binding_group_new ();
     628    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     629    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     630    BindingSource *other;
     631  
     632    g_binding_group_set_source (group, source);
     633    g_binding_group_bind (group, "value",
     634                          target, "value",
     635                          G_BINDING_BIDIRECTIONAL);
     636  
     637    g_object_get (group, "source", &other, NULL);
     638    g_assert_true (other == source);
     639    g_object_unref (other);
     640  
     641    g_object_set (group, "source", NULL, NULL);
     642    g_object_get (group, "source", &other, NULL);
     643    g_assert_null (other);
     644  
     645    g_object_add_weak_pointer (G_OBJECT (target), (gpointer)&target);
     646    g_object_unref (target);
     647    g_assert_null (target);
     648  
     649    g_object_unref (source);
     650    g_object_unref (group);
     651  }
     652  
     653  static void
     654  test_binding_group_weak_notify_no_bindings (void)
     655  {
     656    GBindingGroup *group = g_binding_group_new ();
     657    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     658  
     659    g_binding_group_set_source (group, source);
     660    g_assert_finalize_object (source);
     661    g_assert_finalize_object (group);
     662  }
     663  
     664  static void
     665  test_binding_group_empty_closures (void)
     666  {
     667    GBindingGroup *group = g_binding_group_new ();
     668    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     669    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     670  
     671    g_binding_group_bind_full (group, "value", target, "value", 0,
     672                               NULL, NULL, NULL, NULL);
     673  
     674    g_assert_finalize_object (group);
     675    g_assert_finalize_object (target);
     676    g_assert_finalize_object (source);
     677  }
     678  
     679  gint
     680  main (gint   argc,
     681        gchar *argv[])
     682  {
     683    g_test_init (&argc, &argv, NULL);
     684    g_test_add_func ("/GObject/BindingGroup/invalid", test_binding_group_invalid);
     685    g_test_add_func ("/GObject/BindingGroup/default", test_binding_group_default);
     686    g_test_add_func ("/GObject/BindingGroup/bidirectional", test_binding_group_bidirectional);
     687    g_test_add_func ("/GObject/BindingGroup/transform", test_binding_group_transform);
     688    g_test_add_func ("/GObject/BindingGroup/transform-closures", test_binding_group_transform_closures);
     689    g_test_add_func ("/GObject/BindingGroup/same-object", test_binding_group_same_object);
     690    g_test_add_func ("/GObject/BindingGroup/weak-ref-source", test_binding_group_weak_ref_source);
     691    g_test_add_func ("/GObject/BindingGroup/weak-ref-target", test_binding_group_weak_ref_target);
     692    g_test_add_func ("/GObject/BindingGroup/properties", test_binding_group_properties);
     693    g_test_add_func ("/GObject/BindingGroup/weak-notify-no-bindings", test_binding_group_weak_notify_no_bindings);
     694    g_test_add_func ("/GObject/BindingGroup/empty-closures", test_binding_group_empty_closures);
     695    return g_test_run ();
     696  }