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