Imported Upstream version 2.67.2
[platform/upstream/glib.git] / gobject / tests / properties.c
1 #include <stdlib.h>
2 #include <gstdio.h>
3 #include <glib-object.h>
4
5 typedef struct _TestObject {
6   GObject parent_instance;
7   gint foo;
8   gboolean bar;
9   gchar *baz;
10   gchar *quux;
11 } TestObject;
12
13 typedef struct _TestObjectClass {
14   GObjectClass parent_class;
15 } TestObjectClass;
16
17 enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, PROP_QUUX, N_PROPERTIES };
18
19 static GParamSpec *properties[N_PROPERTIES] = { NULL, };
20
21 static GType test_object_get_type (void);
22 G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
23
24 static void
25 test_object_set_foo (TestObject *obj,
26                      gint        foo)
27 {
28   if (obj->foo != foo)
29     {
30       obj->foo = foo;
31
32       g_assert (properties[PROP_FOO] != NULL);
33       g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
34     }
35 }
36
37 static void
38 test_object_set_bar (TestObject *obj,
39                      gboolean    bar)
40 {
41   bar = !!bar;
42
43   if (obj->bar != bar)
44     {
45       obj->bar = bar;
46
47       g_assert (properties[PROP_BAR] != NULL);
48       g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
49     }
50 }
51
52 static void
53 test_object_set_baz (TestObject  *obj,
54                      const gchar *baz)
55 {
56   if (g_strcmp0 (obj->baz, baz) != 0)
57     {
58       g_free (obj->baz);
59       obj->baz = g_strdup (baz);
60
61       g_assert (properties[PROP_BAZ] != NULL);
62       g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
63     }
64 }
65
66 static void
67 test_object_set_quux (TestObject  *obj,
68                       const gchar *quux)
69 {
70   if (g_strcmp0 (obj->quux, quux) != 0)
71     {
72       g_free (obj->quux);
73       obj->quux = g_strdup (quux);
74
75       g_assert (properties[PROP_QUUX] != NULL);
76       g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_QUUX]);
77     }
78 }
79
80 static void
81 test_object_finalize (GObject *gobject)
82 {
83   TestObject *self = (TestObject *) gobject;
84
85   g_free (self->baz);
86   g_free (self->quux);
87
88   /* When the ref_count of an object is zero it is still
89    * possible to notify the property, but it should do
90    * nothing and silently quit (bug #705570)
91    */
92   g_object_notify (gobject, "foo");
93   g_object_notify_by_pspec (gobject, properties[PROP_BAR]);
94
95   G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
96 }
97
98 static void
99 test_object_set_property (GObject *gobject,
100                           guint prop_id,
101                           const GValue *value,
102                           GParamSpec *pspec)
103 {
104   TestObject *tobj = (TestObject *) gobject;
105
106   g_assert_cmpint (prop_id, !=, 0);
107   g_assert_cmpint (prop_id, !=, N_PROPERTIES);
108   g_assert (pspec == properties[prop_id]);
109
110   switch (prop_id)
111     {
112     case PROP_FOO:
113       test_object_set_foo (tobj, g_value_get_int (value));
114       break;
115
116     case PROP_BAR:
117       test_object_set_bar (tobj, g_value_get_boolean (value));
118       break;
119
120     case PROP_BAZ:
121       test_object_set_baz (tobj, g_value_get_string (value));
122       break;
123
124     case PROP_QUUX:
125       test_object_set_quux (tobj, g_value_get_string (value));
126       break;
127
128     default:
129       g_assert_not_reached ();
130     }
131 }
132
133 static void
134 test_object_get_property (GObject *gobject,
135                           guint prop_id,
136                           GValue *value,
137                           GParamSpec *pspec)
138 {
139   TestObject *tobj = (TestObject *) gobject;
140
141   g_assert_cmpint (prop_id, !=, 0);
142   g_assert_cmpint (prop_id, !=, N_PROPERTIES);
143   g_assert (pspec == properties[prop_id]);
144
145   switch (prop_id)
146     {
147     case PROP_FOO:
148       g_value_set_int (value, tobj->foo);
149       break;
150
151     case PROP_BAR:
152       g_value_set_boolean (value, tobj->bar);
153       break;
154
155     case PROP_BAZ:
156       g_value_set_string (value, tobj->baz);
157       break;
158
159     case PROP_QUUX:
160       g_value_set_string (value, tobj->quux);
161       break;
162
163     default:
164       g_assert_not_reached ();
165     }
166 }
167
168 static void
169 test_object_class_init (TestObjectClass *klass)
170 {
171   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
172
173   properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo",
174                                            -1, G_MAXINT,
175                                            0,
176                                            G_PARAM_READWRITE);
177   properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
178                                                FALSE,
179                                                G_PARAM_READWRITE);
180   properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
181                                               NULL,
182                                               G_PARAM_READWRITE);
183   properties[PROP_QUUX] = g_param_spec_string ("quux", "quux", "quux",
184                                                NULL,
185                                                G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
186
187   gobject_class->set_property = test_object_set_property;
188   gobject_class->get_property = test_object_get_property;
189   gobject_class->finalize = test_object_finalize;
190
191   g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
192 }
193
194 static void
195 test_object_init (TestObject *self)
196 {
197   self->foo = 42;
198   self->bar = TRUE;
199   self->baz = g_strdup ("Hello");
200   self->quux = NULL;
201 }
202
203 static void
204 properties_install (void)
205 {
206   TestObject *obj = g_object_new (test_object_get_type (), NULL);
207   GParamSpec *pspec;
208
209   g_assert (properties[PROP_FOO] != NULL);
210
211   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo");
212   g_assert (properties[PROP_FOO] == pspec);
213
214   g_object_unref (obj);
215 }
216
217 typedef struct {
218   const gchar *name;
219   GParamSpec *pspec;
220   gboolean    fired;
221 } TestNotifyClosure;
222
223 static void
224 on_notify (GObject           *gobject,
225            GParamSpec        *pspec,
226            TestNotifyClosure *closure)
227 {
228   g_assert (closure->pspec == pspec);
229   g_assert_cmpstr (closure->name, ==, pspec->name);
230   closure->fired = TRUE;
231 }
232
233 static void
234 properties_notify (void)
235 {
236   TestObject *obj = g_object_new (test_object_get_type (), NULL);
237   TestNotifyClosure closure;
238
239   g_assert (properties[PROP_FOO] != NULL);
240   g_assert (properties[PROP_QUUX] != NULL);
241   g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &closure);
242
243   closure.name = "foo";
244   closure.pspec = properties[PROP_FOO];
245
246   closure.fired = FALSE;
247   g_object_set (obj, "foo", 47, NULL);
248   g_assert (closure.fired);
249
250   closure.name = "baz";
251   closure.pspec = properties[PROP_BAZ];
252
253   closure.fired = FALSE;
254   g_object_set (obj, "baz", "something new", NULL);
255   g_assert (closure.fired);
256
257   /* baz lacks explicit notify, so we will see this twice */
258   closure.fired = FALSE;
259   g_object_set (obj, "baz", "something new", NULL);
260   g_assert (closure.fired);
261
262   /* quux on the other hand, ... */
263   closure.name = "quux";
264   closure.pspec = properties[PROP_QUUX];
265
266   closure.fired = FALSE;
267   g_object_set (obj, "quux", "something new", NULL);
268   g_assert (closure.fired);
269
270   /* no change; no notify */
271   closure.fired = FALSE;
272   g_object_set (obj, "quux", "something new", NULL);
273   g_assert (!closure.fired);
274
275
276   g_object_unref (obj);
277 }
278
279 typedef struct {
280   GParamSpec *pspec[3];
281   gint pos;
282 } Notifys;
283
284 static void
285 on_notify2 (GObject    *gobject,
286             GParamSpec *pspec,
287             Notifys    *n)
288 {
289   g_assert (n->pspec[n->pos] == pspec);
290   n->pos++;
291 }
292
293 static void
294 properties_notify_queue (void)
295 {
296   TestObject *obj = g_object_new (test_object_get_type (), NULL);
297   Notifys n;
298
299   g_assert (properties[PROP_FOO] != NULL);
300
301   n.pspec[0] = properties[PROP_BAZ];
302   n.pspec[1] = properties[PROP_BAR];
303   n.pspec[2] = properties[PROP_FOO];
304   n.pos = 0;
305
306   g_signal_connect (obj, "notify", G_CALLBACK (on_notify2), &n);
307
308   g_object_freeze_notify (G_OBJECT (obj));
309   g_object_set (obj, "foo", 47, NULL);
310   g_object_set (obj, "bar", TRUE, "foo", 42, "baz", "abc", NULL);
311   g_object_thaw_notify (G_OBJECT (obj));
312   g_assert (n.pos == 3);
313
314   g_object_unref (obj);
315 }
316
317 static void
318 properties_construct (void)
319 {
320   TestObject *obj;
321   gint val;
322   gboolean b;
323   gchar *s;
324
325   g_test_bug ("630357");
326
327   /* more than 16 args triggers a realloc in g_object_new_valist() */
328   obj = g_object_new (test_object_get_type (),
329                       "foo", 1,
330                       "foo", 2,
331                       "foo", 3,
332                       "foo", 4,
333                       "foo", 5,
334                       "bar", FALSE,
335                       "foo", 6,
336                       "foo", 7,
337                       "foo", 8,
338                       "foo", 9,
339                       "foo", 10,
340                       "baz", "boo",
341                       "foo", 11,
342                       "foo", 12,
343                       "foo", 13,
344                       "foo", 14,
345                       "foo", 15,
346                       "foo", 16,
347                       "foo", 17,
348                       "foo", 18,
349                       NULL);
350
351   g_object_get (obj, "foo", &val, NULL);
352   g_assert (val == 18);
353   g_object_get (obj, "bar", &b, NULL);
354   g_assert (!b);
355   g_object_get (obj, "baz", &s, NULL);
356   g_assert_cmpstr (s, ==, "boo");
357   g_free (s);
358
359   g_object_unref (obj);
360 }
361
362 static void
363 properties_testv_with_no_properties (void)
364 {
365   TestObject *test_obj;
366   const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
367   GValue values_out[4] = { G_VALUE_INIT };
368   guint i;
369
370   /* Test newv_with_properties && getv */
371   test_obj = (TestObject *) g_object_new_with_properties (
372       test_object_get_type (), 0, NULL, NULL);
373   g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
374
375   /* It should have init values */
376   g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 42);
377   g_assert_true (g_value_get_boolean (&values_out[1]));
378   g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "Hello");
379   g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, NULL);
380
381   for (i = 0; i < 4; i++)
382     g_value_unset (&values_out[i]);
383   g_object_unref (test_obj);
384 }
385
386 static void
387 properties_testv_with_valid_properties (void)
388 {
389   TestObject *test_obj;
390   const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
391
392   GValue values_in[4] = { G_VALUE_INIT };
393   GValue values_out[4] = { G_VALUE_INIT };
394   guint i;
395
396   g_value_init (&(values_in[0]), G_TYPE_INT);
397   g_value_set_int (&(values_in[0]), 100);
398
399   g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
400   g_value_set_boolean (&(values_in[1]), TRUE);
401
402   g_value_init (&(values_in[2]), G_TYPE_STRING);
403   g_value_set_string (&(values_in[2]), "pigs");
404
405   g_value_init (&(values_in[3]), G_TYPE_STRING);
406   g_value_set_string (&(values_in[3]), "fly");
407
408   /* Test newv_with_properties && getv */
409   test_obj = (TestObject *) g_object_new_with_properties (
410       test_object_get_type (), 4, prop_names, values_in);
411   g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
412
413   g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 100);
414   g_assert_true (g_value_get_boolean (&values_out[1]));
415   g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "pigs");
416   g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, "fly");
417
418   for (i = 0; i < G_N_ELEMENTS (values_out); i++)
419     g_value_unset (&values_out[i]);
420
421   /* Test newv2 && getv */
422   g_value_set_string (&(values_in[2]), "Elmo knows");
423   g_value_set_string (&(values_in[3]), "where you live");
424   g_object_setv (G_OBJECT (test_obj), 4, prop_names, values_in);
425
426   g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out);
427
428   g_assert_cmpint (g_value_get_int (&values_out[0]), ==, 100);
429   g_assert_true (g_value_get_boolean (&values_out[1]));
430
431   g_assert_cmpstr (g_value_get_string (&values_out[2]), ==, "Elmo knows");
432   g_assert_cmpstr (g_value_get_string (&values_out[3]), ==, "where you live");
433
434   for (i = 0; i < G_N_ELEMENTS (values_in); i++)
435     g_value_unset (&values_in[i]);
436   for (i = 0; i < G_N_ELEMENTS (values_out); i++)
437     g_value_unset (&values_out[i]);
438
439   g_object_unref (test_obj);
440 }
441
442 static void
443 properties_testv_with_invalid_property_type (void)
444 {
445   if (g_test_subprocess ())
446     {
447       TestObject *test_obj;
448       const char *invalid_prop_names[1] = { "foo" };
449       GValue values_in[1] = { G_VALUE_INIT };
450
451       g_value_init (&(values_in[0]), G_TYPE_STRING);
452       g_value_set_string (&(values_in[0]), "fly");
453
454       test_obj = (TestObject *) g_object_new_with_properties (
455           test_object_get_type (), 1, invalid_prop_names, values_in);
456       /* should give a warning */
457
458       g_object_unref (test_obj);
459     }
460   g_test_trap_subprocess (NULL, 0, 0);
461   g_test_trap_assert_failed ();
462   g_test_trap_assert_stderr ("*WARNING*foo*gint*gchararray*");
463 }
464
465
466 static void
467 properties_testv_with_invalid_property_names (void)
468 {
469   if (g_test_subprocess ())
470     {
471       TestObject *test_obj;
472       const char *invalid_prop_names[4] = { "foo", "boo", "moo", "poo" };
473       GValue values_in[4] = { G_VALUE_INIT };
474
475       g_value_init (&(values_in[0]), G_TYPE_INT);
476       g_value_set_int (&(values_in[0]), 100);
477
478       g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
479       g_value_set_boolean (&(values_in[1]), TRUE);
480
481       g_value_init (&(values_in[2]), G_TYPE_STRING);
482       g_value_set_string (&(values_in[2]), "pigs");
483
484       g_value_init (&(values_in[3]), G_TYPE_STRING);
485       g_value_set_string (&(values_in[3]), "fly");
486
487       test_obj = (TestObject *) g_object_new_with_properties (
488           test_object_get_type (), 4, invalid_prop_names, values_in);
489       /* This call should give 3 Critical warnings. Actually, a critical warning
490        * shouldn't make g_object_new_with_properties to fail when a bad named
491        * property is given, because, it will just ignore that property. However,
492        * for test purposes, it is considered that the test doesn't pass.
493        */
494
495       g_object_unref (test_obj);
496     }
497
498   g_test_trap_subprocess (NULL, 0, 0);
499   g_test_trap_assert_failed ();
500   g_test_trap_assert_stderr ("*CRITICAL*g_object_new_is_valid_property*boo*");
501 }
502
503 static void
504 properties_testv_getv (void)
505 {
506   TestObject *test_obj;
507   const char *prop_names[4] = { "foo", "bar", "baz", "quux" };
508   GValue values_out_initialized[4] = { G_VALUE_INIT };
509   GValue values_out_uninitialized[4] = { G_VALUE_INIT };
510   guint i;
511
512   g_value_init (&(values_out_initialized[0]), G_TYPE_INT);
513   g_value_init (&(values_out_initialized[1]), G_TYPE_BOOLEAN);
514   g_value_init (&(values_out_initialized[2]), G_TYPE_STRING);
515   g_value_init (&(values_out_initialized[3]), G_TYPE_STRING);
516
517   test_obj = (TestObject *) g_object_new_with_properties (
518       test_object_get_type (), 0, NULL, NULL);
519
520   /* Test g_object_getv for an initialized values array */
521   g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out_initialized);
522   /* It should have init values */
523   g_assert_cmpint (g_value_get_int (&values_out_initialized[0]), ==, 42);
524   g_assert_true (g_value_get_boolean (&values_out_initialized[1]));
525   g_assert_cmpstr (g_value_get_string (&values_out_initialized[2]), ==, "Hello");
526   g_assert_cmpstr (g_value_get_string (&values_out_initialized[3]), ==, NULL);
527
528   /* Test g_object_getv for an uninitialized values array */
529   g_object_getv (G_OBJECT (test_obj), 4, prop_names, values_out_uninitialized);
530   /* It should have init values */
531   g_assert_cmpint (g_value_get_int (&values_out_uninitialized[0]), ==, 42);
532   g_assert_true (g_value_get_boolean (&values_out_uninitialized[1]));
533   g_assert_cmpstr (g_value_get_string (&values_out_uninitialized[2]), ==, "Hello");
534   g_assert_cmpstr (g_value_get_string (&values_out_uninitialized[3]), ==, NULL);
535
536   for (i = 0; i < 4; i++)
537     {
538       g_value_unset (&values_out_initialized[i]);
539       g_value_unset (&values_out_uninitialized[i]);
540     }
541   g_object_unref (test_obj);
542 }
543
544 static void
545 properties_get_property (void)
546 {
547   TestObject *test_obj;
548   struct {
549     const char *name;
550     GType gtype;
551     GValue value;
552   } test_props[] = {
553     { "foo", G_TYPE_INT, G_VALUE_INIT },
554     { "bar", G_TYPE_INVALID, G_VALUE_INIT },
555     { "bar", G_TYPE_STRING, G_VALUE_INIT },
556   };
557   gsize i;
558
559   g_test_summary ("g_object_get_property() accepts uninitialized, "
560                   "initialized, and transformable values");
561
562   for (i = 0; i < G_N_ELEMENTS (test_props); i++)
563     {
564       if (test_props[i].gtype != G_TYPE_INVALID)
565         g_value_init (&(test_props[i].value), test_props[i].gtype);
566     }
567
568   test_obj = (TestObject *) g_object_new_with_properties (test_object_get_type (), 0, NULL, NULL);
569
570   g_test_message ("Test g_object_get_property with an initialized value");
571   g_object_get_property (G_OBJECT (test_obj), test_props[0].name, &(test_props[0].value));
572   g_assert_cmpint (g_value_get_int (&(test_props[0].value)), ==, 42);
573
574   g_test_message ("Test g_object_get_property with an uninitialized value");
575   g_object_get_property (G_OBJECT (test_obj), test_props[1].name, &(test_props[1].value));
576   g_assert_true (g_value_get_boolean (&(test_props[1].value)));
577
578   g_test_message ("Test g_object_get_property with a transformable value");
579   g_object_get_property (G_OBJECT (test_obj), test_props[2].name, &(test_props[2].value));
580   g_assert_true (G_VALUE_HOLDS_STRING (&(test_props[2].value)));
581   g_assert_cmpstr (g_value_get_string (&(test_props[2].value)), ==, "TRUE");
582
583   for (i = 0; i < G_N_ELEMENTS (test_props); i++)
584     g_value_unset (&(test_props[i].value));
585
586   g_object_unref (test_obj);
587 }
588
589 static void
590 properties_testv_notify_queue (void)
591 {
592   TestObject *test_obj;
593   const char *prop_names[3] = { "foo", "bar", "baz" };
594   GValue values_in[3] = { G_VALUE_INIT };
595   Notifys n;
596   guint i;
597
598   g_value_init (&(values_in[0]), G_TYPE_INT);
599   g_value_set_int (&(values_in[0]), 100);
600
601   g_value_init (&(values_in[1]), G_TYPE_BOOLEAN);
602   g_value_set_boolean (&(values_in[1]), TRUE);
603
604   g_value_init (&(values_in[2]), G_TYPE_STRING);
605   g_value_set_string (&(values_in[2]), "");
606
607   /* Test newv_with_properties && getv */
608   test_obj = (TestObject *) g_object_new_with_properties (
609       test_object_get_type (), 0, NULL, NULL);
610
611   g_assert_nonnull (properties[PROP_FOO]);
612
613   n.pspec[0] = properties[PROP_BAZ];
614   n.pspec[1] = properties[PROP_BAR];
615   n.pspec[2] = properties[PROP_FOO];
616   n.pos = 0;
617
618   g_signal_connect (test_obj, "notify", G_CALLBACK (on_notify2), &n);
619
620   g_object_freeze_notify (G_OBJECT (test_obj));
621   {
622     g_object_setv (G_OBJECT (test_obj), 3, prop_names, values_in);
623
624     /* Set "foo" to 70 */
625     g_value_set_int (&(values_in[0]), 100);
626     g_object_setv (G_OBJECT (test_obj), 1, prop_names, values_in);
627   }
628   g_object_thaw_notify (G_OBJECT (test_obj));
629   g_assert_cmpint (n.pos, ==, 3);
630
631   for (i = 0; i < 3; i++)
632     g_value_unset (&values_in[i]);
633   g_object_unref (test_obj);
634 }
635
636 int
637 main (int argc, char *argv[])
638 {
639   g_test_init (&argc, &argv, NULL);
640
641   g_test_bug_base ("http://bugzilla.gnome.org/");
642
643   g_test_add_func ("/properties/install", properties_install);
644   g_test_add_func ("/properties/notify", properties_notify);
645   g_test_add_func ("/properties/notify-queue", properties_notify_queue);
646   g_test_add_func ("/properties/construct", properties_construct);
647   g_test_add_func ("/properties/get-property", properties_get_property);
648
649   g_test_add_func ("/properties/testv_with_no_properties",
650       properties_testv_with_no_properties);
651   g_test_add_func ("/properties/testv_with_valid_properties",
652       properties_testv_with_valid_properties);
653   g_test_add_func ("/properties/testv_with_invalid_property_type",
654       properties_testv_with_invalid_property_type);
655   g_test_add_func ("/properties/testv_with_invalid_property_names",
656       properties_testv_with_invalid_property_names);
657   g_test_add_func ("/properties/testv_getv", properties_testv_getv);
658   g_test_add_func ("/properties/testv_notify_queue",
659       properties_testv_notify_queue);
660
661   return g_test_run ();
662 }