(root)/
glib-2.79.0/
gobject/
tests/
binding.c
       1  #include <stdlib.h>
       2  #include <gstdio.h>
       3  #include <glib-object.h>
       4  
       5  #define assert_cmpsource(binding, op, expected_source) G_STMT_START { \
       6    GObject *tmp, *tmp2; \
       7    tmp = g_binding_dup_source ((binding)); \
       8    G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
       9    tmp2 = g_binding_get_source ((binding)); \
      10    G_GNUC_END_IGNORE_DEPRECATIONS \
      11    g_assert_nonnull (tmp); \
      12    g_assert_true ((gpointer) tmp op (gpointer) (expected_source)); \
      13    g_assert_true (tmp == tmp2); \
      14    g_object_unref (tmp); \
      15  } G_STMT_END
      16  
      17  #define assert_cmptarget(binding, op, expected_target) G_STMT_START { \
      18    GObject *tmp, *tmp2; \
      19    tmp = g_binding_dup_target ((binding)); \
      20    G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
      21    tmp2 = g_binding_get_target ((binding)); \
      22    G_GNUC_END_IGNORE_DEPRECATIONS \
      23    g_assert_nonnull (tmp); \
      24    g_assert_true ((gpointer) tmp op (gpointer) (expected_target)); \
      25    g_assert_true (tmp == tmp2); \
      26    g_object_unref (tmp); \
      27  } G_STMT_END
      28  
      29  typedef struct {
      30    GTypeInterface g_iface;
      31  } FooInterface;
      32  
      33  GType foo_get_type (void);
      34  
      35  G_DEFINE_INTERFACE (Foo, foo, G_TYPE_OBJECT)
      36  
      37  static void
      38  foo_default_init (FooInterface *iface)
      39  {
      40  }
      41  
      42  typedef struct {
      43    GObject parent;
      44  } Baa;
      45  
      46  typedef struct {
      47    GObjectClass parent_class;
      48  } BaaClass;
      49  
      50  static void
      51  baa_init_foo (FooInterface *iface)
      52  {
      53  }
      54  
      55  GType baa_get_type (void);
      56  
      57  G_DEFINE_TYPE_WITH_CODE (Baa, baa, G_TYPE_OBJECT,
      58                           G_IMPLEMENT_INTERFACE (foo_get_type (), baa_init_foo))
      59  
      60  static void
      61  baa_init (Baa *baa)
      62  {
      63  }
      64  
      65  static void
      66  baa_class_init (BaaClass *class)
      67  {
      68  }
      69  
      70  typedef struct _BindingSource
      71  {
      72    GObject parent_instance;
      73  
      74    gint foo;
      75    gint bar;
      76    gdouble double_value;
      77    gboolean toggle;
      78    gpointer item;
      79  } BindingSource;
      80  
      81  typedef struct _BindingSourceClass
      82  {
      83    GObjectClass parent_class;
      84  } BindingSourceClass;
      85  
      86  enum
      87  {
      88    PROP_SOURCE_0,
      89  
      90    PROP_SOURCE_FOO,
      91    PROP_SOURCE_BAR,
      92    PROP_SOURCE_DOUBLE_VALUE,
      93    PROP_SOURCE_TOGGLE,
      94    PROP_SOURCE_OBJECT
      95  };
      96  
      97  static GType binding_source_get_type (void);
      98  G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT)
      99  
     100  static void
     101  binding_source_set_property (GObject      *gobject,
     102                               guint         prop_id,
     103                               const GValue *value,
     104                               GParamSpec   *pspec)
     105  {
     106    BindingSource *source = (BindingSource *) gobject;
     107  
     108    switch (prop_id)
     109      {
     110      case PROP_SOURCE_FOO:
     111        source->foo = g_value_get_int (value);
     112        break;
     113  
     114      case PROP_SOURCE_BAR:
     115        source->bar = g_value_get_int (value);
     116        break;
     117  
     118      case PROP_SOURCE_DOUBLE_VALUE:
     119        source->double_value = g_value_get_double (value);
     120        break;
     121  
     122      case PROP_SOURCE_TOGGLE:
     123        source->toggle = g_value_get_boolean (value);
     124        break;
     125  
     126      case PROP_SOURCE_OBJECT:
     127        source->item = g_value_get_object (value);
     128        break;
     129  
     130      default:
     131        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     132      }
     133  }
     134  
     135  static void
     136  binding_source_get_property (GObject    *gobject,
     137                               guint       prop_id,
     138                               GValue     *value,
     139                               GParamSpec *pspec)
     140  {
     141    BindingSource *source = (BindingSource *) gobject;
     142  
     143    switch (prop_id)
     144      {
     145      case PROP_SOURCE_FOO:
     146        g_value_set_int (value, source->foo);
     147        break;
     148  
     149      case PROP_SOURCE_BAR:
     150        g_value_set_int (value, source->bar);
     151        break;
     152  
     153      case PROP_SOURCE_DOUBLE_VALUE:
     154        g_value_set_double (value, source->double_value);
     155        break;
     156  
     157      case PROP_SOURCE_TOGGLE:
     158        g_value_set_boolean (value, source->toggle);
     159        break;
     160  
     161      case PROP_SOURCE_OBJECT:
     162        g_value_set_object (value, source->item);
     163        break;
     164  
     165      default:
     166        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     167      }
     168  }
     169  
     170  static void
     171  binding_source_class_init (BindingSourceClass *klass)
     172  {
     173    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     174  
     175    gobject_class->set_property = binding_source_set_property;
     176    gobject_class->get_property = binding_source_get_property;
     177  
     178    g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
     179                                     g_param_spec_int ("foo", "Foo", "Foo",
     180                                                       -1, 100,
     181                                                       0,
     182                                                       G_PARAM_READWRITE));
     183    g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
     184                                     g_param_spec_int ("bar", "Bar", "Bar",
     185                                                       -1, 100,
     186                                                       0,
     187                                                       G_PARAM_READWRITE));
     188    g_object_class_install_property (gobject_class, PROP_SOURCE_DOUBLE_VALUE,
     189                                     g_param_spec_double ("double-value", "Value", "Value",
     190                                                          -100.0, 200.0,
     191                                                          0.0,
     192                                                          G_PARAM_READWRITE));
     193    g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
     194                                     g_param_spec_boolean ("toggle", "Toggle", "Toggle",
     195                                                           FALSE,
     196                                                           G_PARAM_READWRITE));
     197    g_object_class_install_property (gobject_class, PROP_SOURCE_OBJECT,
     198                                     g_param_spec_object ("object", "Object", "Object",
     199                                                          G_TYPE_OBJECT,
     200                                                          G_PARAM_READWRITE));
     201  }
     202  
     203  static void
     204  binding_source_init (BindingSource *self)
     205  {
     206  }
     207  
     208  typedef struct _BindingTarget
     209  {
     210    GObject parent_instance;
     211  
     212    gint bar;
     213    gdouble double_value;
     214    gboolean toggle;
     215    gpointer foo;
     216  } BindingTarget;
     217  
     218  typedef struct _BindingTargetClass
     219  {
     220    GObjectClass parent_class;
     221  } BindingTargetClass;
     222  
     223  enum
     224  {
     225    PROP_TARGET_0,
     226  
     227    PROP_TARGET_BAR,
     228    PROP_TARGET_DOUBLE_VALUE,
     229    PROP_TARGET_TOGGLE,
     230    PROP_TARGET_FOO
     231  };
     232  
     233  static GType binding_target_get_type (void);
     234  G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT)
     235  
     236  static void
     237  binding_target_set_property (GObject      *gobject,
     238                               guint         prop_id,
     239                               const GValue *value,
     240                               GParamSpec   *pspec)
     241  {
     242    BindingTarget *target = (BindingTarget *) gobject;
     243  
     244    switch (prop_id)
     245      {
     246      case PROP_TARGET_BAR:
     247        target->bar = g_value_get_int (value);
     248        break;
     249  
     250      case PROP_TARGET_DOUBLE_VALUE:
     251        target->double_value = g_value_get_double (value);
     252        break;
     253  
     254      case PROP_TARGET_TOGGLE:
     255        target->toggle = g_value_get_boolean (value);
     256        break;
     257  
     258      case PROP_TARGET_FOO:
     259        target->foo = g_value_get_object (value);
     260        break;
     261  
     262      default:
     263        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     264      }
     265  }
     266  
     267  static void
     268  binding_target_get_property (GObject    *gobject,
     269                               guint       prop_id,
     270                               GValue     *value,
     271                               GParamSpec *pspec)
     272  {
     273    BindingTarget *target = (BindingTarget *) gobject;
     274  
     275    switch (prop_id)
     276      {
     277      case PROP_TARGET_BAR:
     278        g_value_set_int (value, target->bar);
     279        break;
     280  
     281      case PROP_TARGET_DOUBLE_VALUE:
     282        g_value_set_double (value, target->double_value);
     283        break;
     284  
     285      case PROP_TARGET_TOGGLE:
     286        g_value_set_boolean (value, target->toggle);
     287        break;
     288  
     289      case PROP_TARGET_FOO:
     290        g_value_set_object (value, target->foo);
     291        break;
     292  
     293      default:
     294        G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     295      }
     296  }
     297  
     298  static void
     299  binding_target_class_init (BindingTargetClass *klass)
     300  {
     301    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     302  
     303    gobject_class->set_property = binding_target_set_property;
     304    gobject_class->get_property = binding_target_get_property;
     305  
     306    g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
     307                                     g_param_spec_int ("bar", "Bar", "Bar",
     308                                                       -1, 100,
     309                                                       0,
     310                                                       G_PARAM_READWRITE));
     311    g_object_class_install_property (gobject_class, PROP_TARGET_DOUBLE_VALUE,
     312                                     g_param_spec_double ("double-value", "Value", "Value",
     313                                                          -100.0, 200.0,
     314                                                          0.0,
     315                                                          G_PARAM_READWRITE));
     316    g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
     317                                     g_param_spec_boolean ("toggle", "Toggle", "Toggle",
     318                                                           FALSE,
     319                                                           G_PARAM_READWRITE));
     320    g_object_class_install_property (gobject_class, PROP_TARGET_FOO,
     321                                     g_param_spec_object ("foo", "Foo", "Foo",
     322                                                          foo_get_type (),
     323                                                          G_PARAM_READWRITE));
     324  }
     325  
     326  static void
     327  binding_target_init (BindingTarget *self)
     328  {
     329  }
     330  
     331  static gboolean
     332  celsius_to_fahrenheit (GBinding     *binding,
     333                         const GValue *from_value,
     334                         GValue       *to_value,
     335                         gpointer      user_data G_GNUC_UNUSED)
     336  {
     337    gdouble celsius, fahrenheit;
     338  
     339    g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
     340    g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
     341  
     342    celsius = g_value_get_double (from_value);
     343    fahrenheit = (9 * celsius / 5) + 32.0;
     344  
     345    if (g_test_verbose ())
     346      g_printerr ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
     347  
     348    g_value_set_double (to_value, fahrenheit);
     349  
     350    return TRUE;
     351  }
     352  
     353  static gboolean
     354  fahrenheit_to_celsius (GBinding     *binding,
     355                         const GValue *from_value,
     356                         GValue       *to_value,
     357                         gpointer      user_data G_GNUC_UNUSED)
     358  {
     359    gdouble celsius, fahrenheit;
     360  
     361    g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
     362    g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
     363  
     364    fahrenheit = g_value_get_double (from_value);
     365    celsius = 5 * (fahrenheit - 32.0) / 9;
     366  
     367    if (g_test_verbose ())
     368      g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
     369  
     370    g_value_set_double (to_value, celsius);
     371  
     372    return TRUE;
     373  }
     374  
     375  static void
     376  binding_default (void)
     377  {
     378    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     379    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     380    GBinding *binding;
     381  
     382    binding = g_object_bind_property (source, "foo",
     383                                      target, "bar",
     384                                      G_BINDING_DEFAULT);
     385  
     386    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     387  
     388    assert_cmpsource (binding, ==, source);
     389    assert_cmptarget (binding, ==, target);
     390  
     391    g_assert_cmpstr (g_binding_get_source_property (binding), ==, "foo");
     392    g_assert_cmpstr (g_binding_get_target_property (binding), ==, "bar");
     393    g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
     394  
     395    g_object_set (source, "foo", 42, NULL);
     396    g_assert_cmpint (source->foo, ==, target->bar);
     397  
     398    g_object_set (target, "bar", 47, NULL);
     399    g_assert_cmpint (source->foo, !=, target->bar);
     400  
     401    g_object_unref (binding);
     402  
     403    g_object_set (source, "foo", 0, NULL);
     404    g_assert_cmpint (source->foo, !=, target->bar);
     405  
     406    g_object_unref (source);
     407    g_object_unref (target);
     408    g_assert_null (binding);
     409  }
     410  
     411  static void
     412  binding_canonicalisation (void)
     413  {
     414    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     415    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     416    GBinding *binding;
     417  
     418    g_test_summary ("Test that bindings set up with non-canonical property names work");
     419  
     420    binding = g_object_bind_property (source, "double_value",
     421                                      target, "double_value",
     422                                      G_BINDING_DEFAULT);
     423  
     424    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     425  
     426    assert_cmpsource (binding, ==, source);
     427    assert_cmptarget (binding, ==, target);
     428  
     429    g_assert_cmpstr (g_binding_get_source_property (binding), ==, "double-value");
     430    g_assert_cmpstr (g_binding_get_target_property (binding), ==, "double-value");
     431    g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
     432  
     433    g_object_set (source, "double-value", 24.0, NULL);
     434    g_assert_cmpfloat (target->double_value, ==, source->double_value);
     435  
     436    g_object_set (target, "double-value", 69.0, NULL);
     437    g_assert_cmpfloat (source->double_value, !=, target->double_value);
     438  
     439    g_object_unref (target);
     440    g_object_unref (source);
     441    g_assert_null (binding);
     442  }
     443  
     444  static void
     445  binding_bidirectional (void)
     446  {
     447    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     448    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     449    GBinding *binding;
     450  
     451    binding = g_object_bind_property (source, "foo",
     452                                      target, "bar",
     453                                      G_BINDING_BIDIRECTIONAL);
     454    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     455  
     456    g_object_set (source, "foo", 42, NULL);
     457    g_assert_cmpint (source->foo, ==, target->bar);
     458  
     459    g_object_set (target, "bar", 47, NULL);
     460    g_assert_cmpint (source->foo, ==, target->bar);
     461  
     462    g_object_unref (binding);
     463  
     464    g_object_set (source, "foo", 0, NULL);
     465    g_assert_cmpint (source->foo, !=, target->bar);
     466  
     467    g_object_unref (source);
     468    g_object_unref (target);
     469    g_assert_null (binding);
     470  }
     471  
     472  static void
     473  data_free (gpointer data)
     474  {
     475    gboolean *b = data;
     476  
     477    *b = TRUE;
     478  }
     479  
     480  static void
     481  binding_transform_default (void)
     482  {
     483    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     484    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     485    GBinding *binding;
     486    gpointer src, trg;
     487    gchar *src_prop, *trg_prop;
     488    GBindingFlags flags;
     489  
     490    binding = g_object_bind_property (source, "foo",
     491                                      target, "double-value",
     492                                      G_BINDING_BIDIRECTIONAL);
     493  
     494    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     495  
     496    g_object_get (binding,
     497                  "source", &src,
     498                  "source-property", &src_prop,
     499                  "target", &trg,
     500                  "target-property", &trg_prop,
     501                  "flags", &flags,
     502                  NULL);
     503    g_assert_true (src == source);
     504    g_assert_true (trg == target);
     505    g_assert_cmpstr (src_prop, ==, "foo");
     506    g_assert_cmpstr (trg_prop, ==, "double-value");
     507    g_assert_cmpint (flags, ==, G_BINDING_BIDIRECTIONAL);
     508    g_object_unref (src);
     509    g_object_unref (trg);
     510    g_free (src_prop);
     511    g_free (trg_prop);
     512  
     513    g_object_set (source, "foo", 24, NULL);
     514    g_assert_cmpfloat (target->double_value, ==, 24.0);
     515  
     516    g_object_set (target, "double-value", 69.0, NULL);
     517    g_assert_cmpint (source->foo, ==, 69);
     518  
     519    g_object_unref (target);
     520    g_object_unref (source);
     521    g_assert_null (binding);
     522  }
     523  
     524  static void
     525  binding_transform (void)
     526  {
     527    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     528    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     529    GBinding *binding G_GNUC_UNUSED;
     530    gboolean unused_data = FALSE;
     531  
     532    binding = g_object_bind_property_full (source, "double-value",
     533                                           target, "double-value",
     534                                           G_BINDING_BIDIRECTIONAL,
     535                                           celsius_to_fahrenheit,
     536                                           fahrenheit_to_celsius,
     537                                           &unused_data, data_free);
     538  
     539    g_object_set (source, "double-value", 24.0, NULL);
     540    g_assert_cmpfloat (target->double_value, ==, ((9 * 24.0 / 5) + 32.0));
     541  
     542    g_object_set (target, "double-value", 69.0, NULL);
     543    g_assert_cmpfloat (source->double_value, ==, (5 * (69.0 - 32.0) / 9));
     544  
     545    g_object_unref (source);
     546    g_object_unref (target);
     547  
     548    g_assert_true (unused_data);
     549  }
     550  
     551  static void
     552  binding_transform_closure (void)
     553  {
     554    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     555    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     556    GBinding *binding G_GNUC_UNUSED;
     557    gboolean unused_data_1 = FALSE, unused_data_2 = FALSE;
     558    GClosure *c2f_clos, *f2c_clos;
     559  
     560    c2f_clos = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit), &unused_data_1, (GClosureNotify) data_free);
     561  
     562    f2c_clos = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius), &unused_data_2, (GClosureNotify) data_free);
     563  
     564    binding = g_object_bind_property_with_closures (source, "double-value",
     565                                                    target, "double-value",
     566                                                    G_BINDING_BIDIRECTIONAL,
     567                                                    c2f_clos,
     568                                                    f2c_clos);
     569  
     570    g_object_set (source, "double-value", 24.0, NULL);
     571    g_assert_cmpfloat (target->double_value, ==, ((9 * 24.0 / 5) + 32.0));
     572  
     573    g_object_set (target, "double-value", 69.0, NULL);
     574    g_assert_cmpfloat (source->double_value, ==, (5 * (69.0 - 32.0) / 9));
     575  
     576    g_object_unref (source);
     577    g_object_unref (target);
     578  
     579    g_assert_true (unused_data_1);
     580    g_assert_true (unused_data_2);
     581  }
     582  
     583  static void
     584  binding_chain (void)
     585  {
     586    BindingSource *a = g_object_new (binding_source_get_type (), NULL);
     587    BindingSource *b = g_object_new (binding_source_get_type (), NULL);
     588    BindingSource *c = g_object_new (binding_source_get_type (), NULL);
     589    GBinding *binding_1, *binding_2;
     590  
     591    g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=621782");
     592  
     593    /* A -> B, B -> C */
     594    binding_1 = g_object_bind_property (a, "foo", b, "foo", G_BINDING_BIDIRECTIONAL);
     595    g_object_add_weak_pointer (G_OBJECT (binding_1), (gpointer *) &binding_1);
     596  
     597    binding_2 = g_object_bind_property (b, "foo", c, "foo", G_BINDING_BIDIRECTIONAL);
     598    g_object_add_weak_pointer (G_OBJECT (binding_2), (gpointer *) &binding_2);
     599  
     600    /* verify the chain */
     601    g_object_set (a, "foo", 42, NULL);
     602    g_assert_cmpint (a->foo, ==, b->foo);
     603    g_assert_cmpint (b->foo, ==, c->foo);
     604  
     605    /* unbind A -> B and B -> C */
     606    g_object_unref (binding_1);
     607    g_assert_null (binding_1);
     608    g_object_unref (binding_2);
     609    g_assert_null (binding_2);
     610  
     611    /* bind A -> C directly */
     612    binding_2 = g_object_bind_property (a, "foo", c, "foo", G_BINDING_BIDIRECTIONAL);
     613  
     614    /* verify the chain is broken */
     615    g_object_set (a, "foo", 47, NULL);
     616    g_assert_cmpint (a->foo, !=, b->foo);
     617    g_assert_cmpint (a->foo, ==, c->foo);
     618  
     619    g_object_unref (a);
     620    g_object_unref (b);
     621    g_object_unref (c);
     622  }
     623  
     624  static void
     625  binding_sync_create (void)
     626  {
     627    BindingSource *source = g_object_new (binding_source_get_type (),
     628                                          "foo", 42,
     629                                          NULL);
     630    BindingTarget *target = g_object_new (binding_target_get_type (),
     631                                          "bar", 47,
     632                                          NULL);
     633    GBinding *binding;
     634  
     635    binding = g_object_bind_property (source, "foo",
     636                                      target, "bar",
     637                                      G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
     638  
     639    g_assert_cmpint (source->foo, ==, 42);
     640    g_assert_cmpint (target->bar, ==, 42);
     641  
     642    g_object_set (source, "foo", 47, NULL);
     643    g_assert_cmpint (source->foo, ==, target->bar);
     644  
     645    g_object_unref (binding);
     646  
     647    g_object_set (target, "bar", 49, NULL);
     648  
     649    binding = g_object_bind_property (source, "foo",
     650                                      target, "bar",
     651                                      G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
     652    g_assert_cmpint (source->foo, ==, 47);
     653    g_assert_cmpint (target->bar, ==, 47);
     654  
     655    g_object_unref (source);
     656    g_object_unref (target);
     657  }
     658  
     659  static void
     660  binding_invert_boolean (void)
     661  {
     662    BindingSource *source = g_object_new (binding_source_get_type (),
     663                                          "toggle", TRUE,
     664                                          NULL);
     665    BindingTarget *target = g_object_new (binding_target_get_type (),
     666                                          "toggle", FALSE,
     667                                          NULL);
     668    GBinding *binding;
     669  
     670    binding = g_object_bind_property (source, "toggle",
     671                                      target, "toggle",
     672                                      G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN);
     673  
     674    g_assert_true (source->toggle);
     675    g_assert_false (target->toggle);
     676  
     677    g_object_set (source, "toggle", FALSE, NULL);
     678    g_assert_false (source->toggle);
     679    g_assert_true (target->toggle);
     680  
     681    g_object_set (target, "toggle", FALSE, NULL);
     682    g_assert_true (source->toggle);
     683    g_assert_false (target->toggle);
     684  
     685    g_object_unref (binding);
     686    g_object_unref (source);
     687    g_object_unref (target);
     688  }
     689  
     690  static void
     691  binding_same_object (void)
     692  {
     693    BindingSource *source = g_object_new (binding_source_get_type (),
     694                                          "foo", 100,
     695                                          "bar", 50,
     696                                          NULL);
     697    GBinding *binding;
     698  
     699    binding = g_object_bind_property (source, "foo",
     700                                      source, "bar",
     701                                      G_BINDING_BIDIRECTIONAL);
     702    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     703  
     704    g_object_set (source, "foo", 10, NULL);
     705    g_assert_cmpint (source->foo, ==, 10);
     706    g_assert_cmpint (source->bar, ==, 10);
     707    g_object_set (source, "bar", 30, NULL);
     708    g_assert_cmpint (source->foo, ==, 30);
     709    g_assert_cmpint (source->bar, ==, 30);
     710  
     711    g_object_unref (source);
     712    g_assert_null (binding);
     713  }
     714  
     715  static void
     716  binding_unbind (void)
     717  {
     718    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     719    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     720    GBinding *binding;
     721  
     722    binding = g_object_bind_property (source, "foo",
     723                                      target, "bar",
     724                                      G_BINDING_DEFAULT);
     725    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     726  
     727    g_object_set (source, "foo", 42, NULL);
     728    g_assert_cmpint (source->foo, ==, target->bar);
     729  
     730    g_object_set (target, "bar", 47, NULL);
     731    g_assert_cmpint (source->foo, !=, target->bar);
     732  
     733    g_binding_unbind (binding);
     734    g_assert_null (binding);
     735  
     736    g_object_set (source, "foo", 0, NULL);
     737    g_assert_cmpint (source->foo, !=, target->bar);
     738  
     739    g_object_unref (source);
     740    g_object_unref (target);
     741  
     742  
     743    /* g_binding_unbind() has a special case for this */
     744    source = g_object_new (binding_source_get_type (), NULL);
     745    binding = g_object_bind_property (source, "foo",
     746                                      source, "bar",
     747                                      G_BINDING_DEFAULT);
     748    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     749  
     750    g_binding_unbind (binding);
     751    g_assert_null (binding);
     752  
     753    g_object_unref (source);
     754  }
     755  
     756  /* When source or target die, so does the binding if there is no other ref */
     757  static void
     758  binding_unbind_weak (void)
     759  {
     760    GBinding *binding;
     761    BindingSource *source;
     762    BindingTarget *target;
     763  
     764    /* first source, then target */
     765    source = g_object_new (binding_source_get_type (), NULL);
     766    target = g_object_new (binding_target_get_type (), NULL);
     767    binding = g_object_bind_property (source, "foo",
     768                                      target, "bar",
     769                                      G_BINDING_DEFAULT);
     770    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     771    g_assert_nonnull (binding);
     772    g_object_unref (source);
     773    g_assert_null (binding);
     774    g_object_unref (target);
     775    g_assert_null (binding);
     776  
     777    /* first target, then source */
     778    source = g_object_new (binding_source_get_type (), NULL);
     779    target = g_object_new (binding_target_get_type (), NULL);
     780    binding = g_object_bind_property (source, "foo",
     781                                      target, "bar",
     782                                      G_BINDING_DEFAULT);
     783    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     784    g_assert_nonnull (binding);
     785    g_object_unref (target);
     786    g_assert_null (binding);
     787    g_object_unref (source);
     788    g_assert_null (binding);
     789  
     790    /* target and source are the same */
     791    source = g_object_new (binding_source_get_type (), NULL);
     792    binding = g_object_bind_property (source, "foo",
     793                                      source, "bar",
     794                                      G_BINDING_DEFAULT);
     795    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     796    g_assert_nonnull (binding);
     797    g_object_unref (source);
     798    g_assert_null (binding);
     799  }
     800  
     801  /* Test that every call to unbind() after the first is a noop */
     802  static void
     803  binding_unbind_multiple (void)
     804  {
     805    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     806    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     807    GBinding *binding;
     808    guint i;
     809  
     810    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1373");
     811  
     812    binding = g_object_bind_property (source, "foo",
     813                                      target, "bar",
     814                                      G_BINDING_DEFAULT);
     815    g_object_ref (binding);
     816    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     817    g_assert_nonnull (binding);
     818  
     819    /* this shouldn't crash */
     820    for (i = 0; i < 50; i++)
     821      {
     822        g_binding_unbind (binding);
     823        g_assert_nonnull (binding);
     824      }
     825  
     826    g_object_unref (binding);
     827    g_assert_null (binding);
     828  
     829    g_object_unref (source);
     830    g_object_unref (target);
     831  }
     832  
     833  static void
     834  binding_fail (void)
     835  {
     836    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     837    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     838    GBinding *binding;
     839  
     840    /* double -> boolean is not supported */
     841    binding = g_object_bind_property (source, "double-value",
     842                                      target, "toggle",
     843                                      G_BINDING_DEFAULT);
     844    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
     845  
     846    g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
     847                           "*Unable to convert*double*boolean*");
     848    g_object_set (source, "double-value", 1.0, NULL);
     849    g_test_assert_expected_messages ();
     850  
     851    g_object_unref (source);
     852    g_object_unref (target);
     853    g_assert_null (binding);
     854  }
     855  
     856  static gboolean
     857  transform_to_func (GBinding     *binding,
     858                     const GValue *value_a,
     859                     GValue       *value_b,
     860                     gpointer      user_data)
     861  {
     862    if (g_value_type_compatible (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b)))
     863      {
     864        g_value_copy (value_a, value_b);
     865        return TRUE;
     866      }
     867  
     868    if (g_value_type_transformable (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b)))
     869      {
     870        if (g_value_transform (value_a, value_b))
     871          return TRUE;
     872      }
     873  
     874    return FALSE;
     875  }
     876  
     877  static void
     878  binding_interface (void)
     879  {
     880    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     881    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     882    GObject *baa;
     883    GBinding *binding;
     884    GClosure *transform_to;
     885  
     886    /* binding a generic object property to an interface-valued one */
     887    binding = g_object_bind_property (source, "object",
     888                                      target, "foo",
     889                                      G_BINDING_DEFAULT);
     890  
     891    baa = g_object_new (baa_get_type (), NULL);
     892    g_object_set (source, "object", baa, NULL);
     893    g_object_unref (baa);
     894  
     895    g_binding_unbind (binding);
     896  
     897    /* the same, with a generic marshaller */
     898    transform_to = g_cclosure_new (G_CALLBACK (transform_to_func), NULL, NULL);
     899    g_closure_set_marshal (transform_to, g_cclosure_marshal_generic);
     900    binding = g_object_bind_property_with_closures (source, "object",
     901                                                    target, "foo",
     902                                                    G_BINDING_DEFAULT,
     903                                                    transform_to,
     904                                                    NULL);
     905  
     906    baa = g_object_new (baa_get_type (), NULL);
     907    g_object_set (source, "object", baa, NULL);
     908    g_object_unref (baa);
     909  
     910    g_binding_unbind (binding);
     911  
     912    g_object_unref (source);
     913    g_object_unref (target);
     914  }
     915  
     916  typedef struct {
     917    GThread *thread;
     918    GBinding *binding;
     919    GMutex *lock;
     920    GCond *cond;
     921    gboolean *wait;
     922    gint *count; /* (atomic) */
     923  } ConcurrentUnbindData;
     924  
     925  static gpointer
     926  concurrent_unbind_func (gpointer data)
     927  {
     928    ConcurrentUnbindData *unbind_data = data;
     929  
     930    g_mutex_lock (unbind_data->lock);
     931    g_atomic_int_inc (unbind_data->count);
     932    while (*unbind_data->wait)
     933      g_cond_wait (unbind_data->cond, unbind_data->lock);
     934    g_mutex_unlock (unbind_data->lock);
     935    g_binding_unbind (unbind_data->binding);
     936    g_object_unref (unbind_data->binding);
     937  
     938    return NULL;
     939  }
     940  
     941  static void
     942  binding_concurrent_unbind (void)
     943  {
     944    guint i, j;
     945  
     946    g_test_summary ("Test that unbinding from multiple threads concurrently works correctly");
     947  
     948    for (i = 0; i < 50; i++)
     949      {
     950        BindingSource *source = g_object_new (binding_source_get_type (), NULL);
     951        BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
     952        GBinding *binding;
     953        GQueue threads = G_QUEUE_INIT;
     954        GMutex lock;
     955        GCond cond;
     956        gboolean wait = TRUE;
     957        gint count = 0; /* (atomic) */
     958        ConcurrentUnbindData *data;
     959  
     960        g_mutex_init (&lock);
     961        g_cond_init (&cond);
     962  
     963        binding = g_object_bind_property (source, "foo",
     964                                          target, "bar",
     965                                          G_BINDING_BIDIRECTIONAL);
     966        g_object_ref (binding);
     967  
     968        for (j = 0; j < 10; j++)
     969          {
     970            data = g_new0 (ConcurrentUnbindData, 1);
     971  
     972            data->binding = g_object_ref (binding);
     973            data->lock = &lock;
     974            data->cond = &cond;
     975            data->wait = &wait;
     976            data->count = &count;
     977  
     978            data->thread = g_thread_new ("binding-concurrent", concurrent_unbind_func, data);
     979            g_queue_push_tail (&threads, data);
     980          }
     981  
     982        /* wait until all threads are started */
     983        while (g_atomic_int_get (&count) < 10)
     984          g_thread_yield ();
     985  
     986        g_mutex_lock (&lock);
     987        wait = FALSE;
     988        g_cond_broadcast (&cond);
     989        g_mutex_unlock (&lock);
     990  
     991        while ((data = g_queue_pop_head (&threads)))
     992          {
     993            g_thread_join (data->thread);
     994            g_free (data);
     995          }
     996  
     997        g_mutex_clear (&lock);
     998        g_cond_clear (&cond);
     999  
    1000        g_object_unref (binding);
    1001        g_object_unref (source);
    1002        g_object_unref (target);
    1003      }
    1004  }
    1005  
    1006  typedef struct {
    1007    GObject *object;
    1008    GMutex *lock;
    1009    GCond *cond;
    1010    gint *count; /* (atomic) */
    1011    gboolean *wait;
    1012  } ConcurrentFinalizeData;
    1013  
    1014  static gpointer
    1015  concurrent_finalize_func (gpointer data)
    1016  {
    1017    ConcurrentFinalizeData *finalize_data = data;
    1018  
    1019    g_mutex_lock (finalize_data->lock);
    1020    g_atomic_int_inc (finalize_data->count);
    1021    while (*finalize_data->wait)
    1022      g_cond_wait (finalize_data->cond, finalize_data->lock);
    1023    g_mutex_unlock (finalize_data->lock);
    1024    g_object_unref (finalize_data->object);
    1025    g_free (finalize_data);
    1026  
    1027    return NULL;
    1028  }
    1029  
    1030  static void
    1031  binding_concurrent_finalizing (void)
    1032  {
    1033    guint i;
    1034  
    1035    g_test_summary ("Test that finalizing source/target from multiple threads concurrently works correctly");
    1036  
    1037    for (i = 0; i < 50; i++)
    1038      {
    1039        BindingSource *source = g_object_new (binding_source_get_type (), NULL);
    1040        BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
    1041        GBinding *binding;
    1042        GMutex lock;
    1043        GCond cond;
    1044        gboolean wait = TRUE;
    1045        ConcurrentFinalizeData *data;
    1046        GThread *source_thread, *target_thread;
    1047        gint count = 0; /* (atomic) */
    1048  
    1049        g_mutex_init (&lock);
    1050        g_cond_init (&cond);
    1051  
    1052        binding = g_object_bind_property (source, "foo",
    1053                                          target, "bar",
    1054                                          G_BINDING_BIDIRECTIONAL);
    1055        g_object_ref (binding);
    1056  
    1057        data = g_new0 (ConcurrentFinalizeData, 1);
    1058        data->object = (GObject *) source;
    1059        data->wait = &wait;
    1060        data->lock = &lock;
    1061        data->cond = &cond;
    1062        data->count = &count;
    1063        source_thread = g_thread_new ("binding-concurrent", concurrent_finalize_func, data);
    1064  
    1065        data = g_new0 (ConcurrentFinalizeData, 1);
    1066        data->object = (GObject *) target;
    1067        data->wait = &wait;
    1068        data->lock = &lock;
    1069        data->cond = &cond;
    1070        data->count = &count;
    1071        target_thread = g_thread_new ("binding-concurrent", concurrent_finalize_func, data);
    1072  
    1073        /* wait until all threads are started */
    1074        while (g_atomic_int_get (&count) < 2)
    1075          g_thread_yield ();
    1076  
    1077        g_mutex_lock (&lock);
    1078        wait = FALSE;
    1079        g_cond_broadcast (&cond);
    1080        g_mutex_unlock (&lock);
    1081  
    1082        g_thread_join (source_thread);
    1083        g_thread_join (target_thread);
    1084  
    1085        g_mutex_clear (&lock);
    1086        g_cond_clear (&cond);
    1087  
    1088        g_object_unref (binding);
    1089      }
    1090  }
    1091  
    1092  static void
    1093  binding_dispose_source (void)
    1094  {
    1095    /* Test that the source can be disposed */
    1096    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
    1097    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
    1098    GBinding *binding;
    1099  
    1100    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2676");
    1101  
    1102    binding = g_object_bind_property (source, "foo",
    1103                                      target, "bar",
    1104                                      G_BINDING_DEFAULT);
    1105  
    1106    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
    1107  
    1108    g_object_run_dispose (G_OBJECT (source));
    1109    g_assert_null (binding);
    1110  
    1111    g_object_unref (target);
    1112    g_object_unref (source);
    1113  }
    1114  
    1115  static void
    1116  binding_dispose_target (void)
    1117  {
    1118    /* Test that the target can be disposed */
    1119    BindingSource *source = g_object_new (binding_source_get_type (), NULL);
    1120    BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
    1121    GBinding *binding;
    1122  
    1123    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2676");
    1124  
    1125    binding = g_object_bind_property (source, "foo",
    1126                                      target, "bar",
    1127                                      G_BINDING_DEFAULT);
    1128  
    1129    g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
    1130  
    1131    g_object_run_dispose (G_OBJECT (target));
    1132    g_assert_null (binding);
    1133  
    1134    g_object_unref (target);
    1135    g_object_unref (source);
    1136  }
    1137  
    1138  int
    1139  main (int argc, char *argv[])
    1140  {
    1141    g_test_init (&argc, &argv, NULL);
    1142  
    1143    g_test_add_func ("/binding/default", binding_default);
    1144    g_test_add_func ("/binding/canonicalisation", binding_canonicalisation);
    1145    g_test_add_func ("/binding/bidirectional", binding_bidirectional);
    1146    g_test_add_func ("/binding/transform", binding_transform);
    1147    g_test_add_func ("/binding/transform-default", binding_transform_default);
    1148    g_test_add_func ("/binding/transform-closure", binding_transform_closure);
    1149    g_test_add_func ("/binding/chain", binding_chain);
    1150    g_test_add_func ("/binding/sync-create", binding_sync_create);
    1151    g_test_add_func ("/binding/invert-boolean", binding_invert_boolean);
    1152    g_test_add_func ("/binding/same-object", binding_same_object);
    1153    g_test_add_func ("/binding/unbind", binding_unbind);
    1154    g_test_add_func ("/binding/unbind-weak", binding_unbind_weak);
    1155    g_test_add_func ("/binding/unbind-multiple", binding_unbind_multiple);
    1156    g_test_add_func ("/binding/fail", binding_fail);
    1157    g_test_add_func ("/binding/interface", binding_interface);
    1158    g_test_add_func ("/binding/concurrent-unbind", binding_concurrent_unbind);
    1159    g_test_add_func ("/binding/concurrent-finalizing", binding_concurrent_finalizing);
    1160    g_test_add_func ("/binding/dispose-source", binding_dispose_source);
    1161    g_test_add_func ("/binding/dispose-target", binding_dispose_target);
    1162  
    1163    return g_test_run ();
    1164  }