gkdbus: Fix underflow and unreachable code bug
[platform/upstream/glib.git] / 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 }