(root)/
glib-2.79.0/
gobject/
tests/
notify-init.c
       1  /* GLib testing framework examples and tests
       2   * Copyright (C) 2022 Red Hat, Inc.
       3   *
       4   * SPDX-License-Identifier: LicenseRef-old-glib-tests
       5   *
       6   * This work is provided "as is"; redistribution and modification
       7   * in whole or in part, in any medium, physical or electronic is
       8   * permitted without restriction.
       9   *
      10   * This work is distributed in the hope that it will be useful,
      11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13   *
      14   * In no event shall the authors or contributors be liable for any
      15   * direct, indirect, incidental, special, exemplary, or consequential
      16   * damages (including, but not limited to, procurement of substitute
      17   * goods or services; loss of use, data, or profits; or business
      18   * interruption) however caused and on any theory of liability, whether
      19   * in contract, strict liability, or tort (including negligence or
      20   * otherwise) arising in any way out of the use of this software, even
      21   * if advised of the possibility of such damage.
      22   */
      23  
      24  #include <stdlib.h>
      25  #include <gstdio.h>
      26  #include <glib-object.h>
      27  
      28  typedef struct {
      29    GObject parent_instance;
      30    gint foo;
      31    gboolean bar;
      32    gchar *baz;
      33    gchar *quux;
      34  } TestObject;
      35  
      36  typedef struct {
      37    GObjectClass parent_class;
      38  } TestObjectClass;
      39  
      40  typedef enum {
      41    PROP_FOO = 1,
      42    PROP_BAR,
      43    PROP_BAZ,
      44    PROP_QUUX,
      45    N_PROPERTIES
      46  } TestObjectProperty;
      47  
      48  static GParamSpec *properties[N_PROPERTIES] = { NULL, };
      49  
      50  static GType test_object_get_type (void);
      51  G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
      52  
      53  static void
      54  test_object_set_foo (TestObject *obj,
      55                       gint        foo)
      56  {
      57    if (obj->foo != foo)
      58      {
      59        obj->foo = foo;
      60  
      61        g_assert (properties[PROP_FOO] != NULL);
      62        g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
      63      }
      64  }
      65  
      66  static void
      67  test_object_set_bar (TestObject *obj,
      68                       gboolean    bar)
      69  {
      70    bar = !!bar;
      71  
      72    if (obj->bar != bar)
      73      {
      74        obj->bar = bar;
      75  
      76        g_assert (properties[PROP_BAR] != NULL);
      77        g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
      78      }
      79  }
      80  
      81  static void
      82  test_object_set_baz (TestObject  *obj,
      83                       const gchar *baz)
      84  {
      85    if (g_strcmp0 (obj->baz, baz) != 0)
      86      {
      87        g_free (obj->baz);
      88        obj->baz = g_strdup (baz);
      89  
      90        g_assert (properties[PROP_BAZ] != NULL);
      91        g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
      92      }
      93  }
      94  
      95  static void
      96  test_object_set_quux (TestObject  *obj,
      97                        const gchar *quux)
      98  {
      99    if (g_strcmp0 (obj->quux, quux) != 0)
     100      {
     101        g_free (obj->quux);
     102        obj->quux = g_strdup (quux);
     103  
     104        g_assert (properties[PROP_QUUX] != NULL);
     105        g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_QUUX]);
     106      }
     107  }
     108  
     109  static void
     110  test_object_finalize (GObject *gobject)
     111  {
     112    TestObject *self = (TestObject *) gobject;
     113  
     114    g_free (self->baz);
     115    g_free (self->quux);
     116  
     117    G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
     118  }
     119  
     120  static void
     121  test_object_set_property (GObject *gobject,
     122                            guint prop_id,
     123                            const GValue *value,
     124                            GParamSpec *pspec)
     125  {
     126    TestObject *tobj = (TestObject *) gobject;
     127  
     128    g_assert_cmpint (prop_id, !=, 0);
     129    g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
     130  
     131    switch ((TestObjectProperty)prop_id)
     132      {
     133      case PROP_FOO:
     134        test_object_set_foo (tobj, g_value_get_int (value));
     135        break;
     136  
     137      case PROP_BAR:
     138        test_object_set_bar (tobj, g_value_get_boolean (value));
     139        break;
     140  
     141      case PROP_BAZ:
     142        test_object_set_baz (tobj, g_value_get_string (value));
     143        break;
     144  
     145      case PROP_QUUX:
     146        test_object_set_quux (tobj, g_value_get_string (value));
     147        break;
     148  
     149      default:
     150        g_assert_not_reached ();
     151      }
     152  }
     153  
     154  static void
     155  test_object_get_property (GObject *gobject,
     156                            guint prop_id,
     157                            GValue *value,
     158                            GParamSpec *pspec)
     159  {
     160    TestObject *tobj = (TestObject *) gobject;
     161  
     162    g_assert_cmpint (prop_id, !=, 0);
     163    g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
     164  
     165    switch ((TestObjectProperty)prop_id)
     166      {
     167      case PROP_FOO:
     168        g_value_set_int (value, tobj->foo);
     169        break;
     170  
     171      case PROP_BAR:
     172        g_value_set_boolean (value, tobj->bar);
     173        break;
     174  
     175      case PROP_BAZ:
     176        g_value_set_string (value, tobj->baz);
     177        break;
     178  
     179      case PROP_QUUX:
     180        g_value_set_string (value, tobj->quux);
     181        break;
     182  
     183      default:
     184        g_assert_not_reached ();
     185      }
     186  }
     187  
     188  static void
     189  test_object_class_init (TestObjectClass *klass)
     190  {
     191    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     192  
     193    properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo",
     194                                             -1, G_MAXINT,
     195                                             0,
     196                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
     197    properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
     198                                                 FALSE,
     199                                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
     200    properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
     201                                                NULL,
     202                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
     203  
     204    properties[PROP_QUUX] = g_param_spec_string ("quux", "quux", "quux",
     205                                                 NULL,
     206                                                 G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
     207  
     208    gobject_class->set_property = test_object_set_property;
     209    gobject_class->get_property = test_object_get_property;
     210    gobject_class->finalize = test_object_finalize;
     211  
     212    g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
     213  }
     214  
     215  static void
     216  quux_changed (TestObject *self,
     217                GParamSpec *pspec,
     218                gpointer    data)
     219  {
     220    g_assert (self->baz != NULL);
     221  }
     222  
     223  static void
     224  test_object_init (TestObject *self)
     225  {
     226    /* This instance init behavior is the thing we are testing:
     227     *
     228     * 1. Connect to notify::quux
     229     * 2. Change the the quux property
     230     * 3. Continue to set up things that the quux_changed handler
     231     *   relies on
     232     *
     233     * The expected behavior is that:
     234     *
     235     * - The quux_changed handler *is* called
     236     * - It is only called after the object is fully constructed
     237     */
     238    g_signal_connect (self, "notify::quux", G_CALLBACK (quux_changed), NULL);
     239  
     240    test_object_set_quux (self, "quux");
     241  
     242    self->foo = 42;
     243    self->bar = TRUE;
     244    self->baz = g_strdup ("Hello");
     245  }
     246  
     247  static void
     248  test_notify_in_init (void)
     249  {
     250    TestObject *obj;
     251  
     252    g_test_summary ("Test that emitting notify with a handler already connected in test_object_init() works");
     253    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2665");
     254  
     255    obj = g_object_new (test_object_get_type (), NULL);
     256  
     257    g_object_unref (obj);
     258  }
     259  
     260  int
     261  main (int argc, char *argv[])
     262  {
     263    g_test_init (&argc, &argv, NULL);
     264  
     265    g_test_add_func ("/properties/notify-in-init", test_notify_in_init);
     266  
     267    return g_test_run ();
     268  }