1  /* GObject - GLib Type, Object, Parameter and Signal Library
       2   * Copyright (C) 2001, 2003 Red Hat, Inc.
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General
      17   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   */
      19  
      20  #include <stdlib.h>
      21  #include <string.h>
      22  
      23  #include <glib-object.h>
      24  
      25  #include "testcommon.h"
      26  
      27  /* This test tests interface properties, implementing interface
      28   * properties and #GParamSpecOverride.
      29   *
      30   * Four properties are tested:
      31   *
      32   * prop1: Defined in TestIface, Implemented in BaseObject with a GParamSpecOverride
      33   * prop2: Defined in TestIface, Implemented in BaseObject with a new property
      34   * prop3: Defined in TestIface, Implemented in BaseObject, Overridden in DerivedObject
      35   * prop4: Defined in BaseObject, Overridden in DerivedObject
      36   */
      37  
      38  static GType base_object_get_type (void);
      39  static GType derived_object_get_type (void);
      40  
      41  enum {
      42    BASE_PROP_0,
      43    BASE_PROP1,
      44    BASE_PROP2,
      45    BASE_PROP3,
      46    BASE_PROP4
      47  };
      48  
      49  enum {
      50    DERIVED_PROP_0,
      51    DERIVED_PROP3,
      52    DERIVED_PROP4
      53  };
      54  
      55  /*
      56   * BaseObject, a parent class for DerivedObject
      57   */
      58  #define BASE_TYPE_OBJECT          (base_object_get_type ())
      59  #define BASE_OBJECT(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE_TYPE_OBJECT, BaseObject))
      60  typedef struct _BaseObject        BaseObject;
      61  typedef struct _BaseObjectClass   BaseObjectClass;
      62  
      63  struct _BaseObject
      64  {
      65    GObject parent_instance;
      66  
      67    gint val1;
      68    gint val2;
      69    gint val3;
      70    gint val4;
      71  };
      72  struct _BaseObjectClass
      73  {
      74    GObjectClass parent_class;
      75  };
      76  
      77  GObjectClass *base_parent_class;
      78  
      79  /*
      80   * DerivedObject, the child class of DerivedObject
      81   */
      82  #define DERIVED_TYPE_OBJECT          (derived_object_get_type ())
      83  typedef struct _DerivedObject        DerivedObject;
      84  typedef struct _DerivedObjectClass   DerivedObjectClass;
      85  
      86  struct _DerivedObject
      87  {
      88    BaseObject parent_instance;
      89  };
      90  struct _DerivedObjectClass
      91  {
      92    BaseObjectClass parent_class;
      93  };
      94  
      95  /*
      96   * The interface
      97   */
      98  typedef struct _TestIfaceClass TestIfaceClass;
      99  
     100  struct _TestIfaceClass
     101  {
     102    GTypeInterface base_iface;
     103  };
     104  
     105  #define TEST_TYPE_IFACE (test_iface_get_type ())
     106  
     107  /* The paramspecs installed on our interface
     108   */
     109  static GParamSpec *iface_spec1, *iface_spec2, *iface_spec3;
     110  
     111  /* The paramspecs inherited by our derived object
     112   */
     113  static GParamSpec *inherited_spec1, *inherited_spec2, *inherited_spec3, *inherited_spec4;
     114  
     115  static void
     116  test_iface_default_init (TestIfaceClass *iface_vtable)
     117  {
     118    inherited_spec1 = iface_spec1 = g_param_spec_int ("prop1",
     119                                                      "Prop1",
     120                                                      "Property 1",
     121                                                      G_MININT, /* min */
     122                                                      0xFFFF,  /* max */
     123                                                      42,       /* default */
     124                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
     125    g_object_interface_install_property (iface_vtable, iface_spec1);
     126  
     127    iface_spec2 = g_param_spec_int ("prop2",
     128                                    "Prop2",
     129                                    "Property 2",
     130                                    G_MININT, /* min */
     131                                    G_MAXINT, /* max */
     132                                    0,           /* default */
     133                                    G_PARAM_WRITABLE);
     134    g_object_interface_install_property (iface_vtable, iface_spec2);
     135      
     136    inherited_spec3 = iface_spec3 = g_param_spec_int ("prop3",
     137                                                      "Prop3",
     138                                                      "Property 3",
     139                                                      G_MININT, /* min */
     140                                                      G_MAXINT, /* max */
     141                                                      0,         /* default */
     142                                                      G_PARAM_READWRITE);
     143    g_object_interface_install_property (iface_vtable, iface_spec3);
     144  }
     145  
     146  static DEFINE_IFACE (TestIface, test_iface, NULL, test_iface_default_init)
     147  
     148  
     149  static GObject*
     150  base_object_constructor (GType                  type,
     151                           guint                  n_construct_properties,
     152                           GObjectConstructParam *construct_properties)
     153  {
     154    /* The constructor is the one place where a GParamSpecOverride is visible
     155     * to the outside world, so we do a bunch of checks here
     156     */
     157    GValue value1 = G_VALUE_INIT;
     158    GValue value2 = G_VALUE_INIT;
     159    GParamSpec *pspec;
     160  
     161    g_assert (n_construct_properties == 1);
     162  
     163    pspec = construct_properties->pspec;
     164  
     165    /* Check we got the param spec we expected
     166     */
     167    g_assert (G_IS_PARAM_SPEC_OVERRIDE (pspec));
     168    g_assert (pspec->param_id == BASE_PROP1);
     169    g_assert (strcmp (g_param_spec_get_name (pspec), "prop1") == 0);
     170    g_assert (g_param_spec_get_redirect_target (pspec) == iface_spec1);
     171  
     172    /* Test redirection of the nick and blurb to the redirect target
     173     */
     174    g_assert (strcmp (g_param_spec_get_nick (pspec), "Prop1") == 0);
     175    g_assert (strcmp (g_param_spec_get_blurb (pspec), "Property 1") == 0);
     176  
     177    /* Test forwarding of the various GParamSpec methods to the redirect target
     178     */
     179    g_value_init (&value1, G_TYPE_INT);
     180    g_value_init (&value2, G_TYPE_INT);
     181  
     182    g_param_value_set_default (pspec, &value1);
     183    g_assert (g_value_get_int (&value1) == 42);
     184  
     185    g_value_reset (&value1);
     186    g_value_set_int (&value1, 0x10000);
     187    g_assert (g_param_value_validate (pspec, &value1));
     188    g_assert (g_value_get_int (&value1) == 0xFFFF);
     189    g_assert (!g_param_value_validate (pspec, &value1));
     190  
     191    g_value_reset (&value1);
     192    g_value_set_int (&value1, 1);
     193    g_value_set_int (&value2, 2);
     194    g_assert (g_param_values_cmp (pspec, &value1, &value2) < 0);
     195    g_assert (g_param_values_cmp (pspec, &value2, &value1) > 0);
     196  
     197    g_value_unset (&value1);
     198    g_value_unset (&value2);
     199  
     200    return base_parent_class->constructor (type,
     201                                           n_construct_properties,
     202                                           construct_properties);
     203  }
     204  
     205  static void
     206  base_object_set_property (GObject      *object,
     207                            guint         prop_id,
     208                            const GValue *value,
     209                            GParamSpec   *pspec)
     210  {
     211    BaseObject *base_object = BASE_OBJECT (object);
     212  
     213    switch (prop_id)
     214      {
     215      case BASE_PROP1:
     216        g_assert (pspec == inherited_spec1);
     217        base_object->val1 = g_value_get_int (value);
     218        break;
     219      case BASE_PROP2:
     220        g_assert (pspec == inherited_spec2);
     221        base_object->val2 = g_value_get_int (value);
     222        break;
     223      case BASE_PROP3:
     224        g_assert_not_reached ();
     225        break;
     226      case BASE_PROP4:
     227        g_assert_not_reached ();
     228        break;
     229      default:
     230        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     231        break;
     232      }
     233  }
     234  
     235  static void
     236  base_object_get_property (GObject    *object,
     237                            guint       prop_id,
     238                            GValue     *value,
     239                            GParamSpec *pspec)
     240  {
     241    BaseObject *base_object = BASE_OBJECT (object);
     242  
     243    switch (prop_id)
     244      {
     245      case BASE_PROP1:
     246        g_assert (pspec == inherited_spec1);
     247        g_value_set_int (value, base_object->val1);
     248        break;
     249      case BASE_PROP2:
     250        g_assert (pspec == inherited_spec2);
     251        g_value_set_int (value, base_object->val2);
     252        break;
     253      case BASE_PROP3:
     254        g_assert_not_reached ();
     255        break;
     256      case BASE_PROP4:
     257        g_assert_not_reached ();
     258        break;
     259      default:
     260        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     261        break;
     262      }
     263  }
     264  
     265  static void
     266  base_object_notify (GObject    *object,
     267                      GParamSpec *pspec)
     268  {
     269    /* The property passed to notify is the redirect target, not the
     270     * GParamSpecOverride
     271     */
     272    g_assert (pspec == inherited_spec1 ||
     273              pspec == inherited_spec2 ||
     274              pspec == inherited_spec3 ||
     275              pspec == inherited_spec4);
     276  }
     277  
     278  static void
     279  base_object_class_init (BaseObjectClass *class)
     280  {
     281    GObjectClass *object_class = G_OBJECT_CLASS (class);
     282  
     283    base_parent_class= g_type_class_peek_parent (class);
     284  
     285    object_class->constructor = base_object_constructor;
     286    object_class->set_property = base_object_set_property;
     287    object_class->get_property = base_object_get_property;
     288    object_class->notify = base_object_notify;
     289  
     290    g_object_class_override_property (object_class, BASE_PROP1, "prop1");
     291  
     292    /* We override this one using a real property, not GParamSpecOverride
     293     * We change the flags from READONLY to READWRITE to show that we
     294     * can make the flags less restrictive
     295     */
     296    inherited_spec2 = g_param_spec_int ("prop2",
     297                                        "Prop2",
     298                                        "Property 2",
     299                                        G_MININT, /* min */
     300                                        G_MAXINT, /* max */
     301                                        0,        /* default */
     302                                        G_PARAM_READWRITE);
     303    g_object_class_install_property (object_class, BASE_PROP2, inherited_spec2);
     304  
     305    g_object_class_override_property (object_class, BASE_PROP3, "prop3");
     306  
     307    inherited_spec4 = g_param_spec_int ("prop4",
     308                                        "Prop4",
     309                                        "Property 4",
     310                                        G_MININT, /* min */
     311                                        G_MAXINT, /* max */
     312                                        0,        /* default */
     313                                        G_PARAM_READWRITE);
     314    g_object_class_install_property (object_class, BASE_PROP4, inherited_spec4);
     315  }
     316  
     317  static void
     318  base_object_init (BaseObject *base_object)
     319  {
     320    base_object->val1 = 42;
     321  }
     322  
     323  static DEFINE_TYPE_FULL (BaseObject, base_object,
     324                           base_object_class_init, NULL, base_object_init,
     325                           G_TYPE_OBJECT,
     326                           INTERFACE (NULL, TEST_TYPE_IFACE))
     327  
     328  static void
     329  derived_object_set_property (GObject      *object,
     330                               guint         prop_id,
     331                               const GValue *value,
     332                               GParamSpec   *pspec)
     333  {
     334    BaseObject *base_object = BASE_OBJECT (object);
     335  
     336    switch (prop_id)
     337      {
     338      case DERIVED_PROP3:
     339        g_assert (pspec == inherited_spec3);
     340        base_object->val3 = g_value_get_int (value);
     341        break;
     342      case DERIVED_PROP4:
     343        g_assert (pspec == inherited_spec4);
     344        base_object->val4 = g_value_get_int (value);
     345        break;
     346      default:
     347        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     348        break;
     349      }
     350  }
     351  
     352  static void
     353  derived_object_get_property (GObject    *object,
     354                               guint       prop_id,
     355                               GValue     *value,
     356                               GParamSpec *pspec)
     357  {
     358    BaseObject *base_object = BASE_OBJECT (object);
     359  
     360    switch (prop_id)
     361      {
     362      case DERIVED_PROP3:
     363        g_assert (pspec == inherited_spec3);
     364        g_value_set_int (value, base_object->val3);
     365        break;
     366      case DERIVED_PROP4:
     367        g_assert (pspec == inherited_spec4);
     368        g_value_set_int (value, base_object->val4);
     369        break;
     370      default:
     371        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     372        break;
     373      }
     374  }
     375  
     376  static void
     377  derived_object_class_init (DerivedObjectClass *class)
     378  {
     379    GObjectClass *object_class = G_OBJECT_CLASS (class);
     380  
     381    object_class->set_property = derived_object_set_property;
     382    object_class->get_property = derived_object_get_property;
     383  
     384    /* Overriding a property that is itself overriding an interface property */
     385    g_object_class_override_property (object_class, DERIVED_PROP3, "prop3");
     386  
     387    /* Overriding a property not from an interface */
     388    g_object_class_override_property (object_class, DERIVED_PROP4, "prop4");
     389  }
     390  
     391  static DEFINE_TYPE (DerivedObject, derived_object,
     392                      derived_object_class_init, NULL, NULL,
     393                      BASE_TYPE_OBJECT)
     394  
     395  /* Helper function for testing ...list_properties() */
     396  static void
     397  assert_in_properties (GParamSpec  *param_spec,
     398                        GParamSpec **properties,
     399                        gint         n_properties)
     400  {
     401    gint i;
     402    gboolean found = FALSE;
     403  
     404    for (i = 0; i < n_properties; i++)
     405      {
     406        if (properties[i] == param_spec)
     407          found = TRUE;
     408      }
     409  
     410    g_assert (found);
     411  }
     412  
     413  /* Test setting and getting the properties */
     414  static void
     415  test_set (void)
     416  {
     417    BaseObject *object;
     418    gint val1, val2, val3, val4;
     419  
     420    object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
     421  
     422    g_object_set (object,
     423                  "prop1", 0x0101,
     424                  "prop2", 0x0202,
     425                  "prop3", 0x0303,
     426                  "prop4", 0x0404,
     427                  NULL);
     428    g_object_get (object,
     429                  "prop1", &val1,
     430                  "prop2", &val2,
     431                  "prop3", &val3,
     432                  "prop4", &val4,
     433                  NULL);
     434  
     435    g_assert (val1 == 0x0101);
     436    g_assert (val2 == 0x0202);
     437    g_assert (val3 == 0x0303);
     438    g_assert (val4 == 0x0404);
     439  
     440    g_object_unref (object);
     441  }
     442  
     443  /* Test that the right spec is passed on explicit notifications */
     444  static void
     445  test_notify (void)
     446  {
     447    BaseObject *object;
     448  
     449    object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
     450  
     451    g_object_freeze_notify (G_OBJECT (object));
     452    g_object_notify (G_OBJECT (object), "prop1");
     453    g_object_notify (G_OBJECT (object), "prop2");
     454    g_object_notify (G_OBJECT (object), "prop3");
     455    g_object_notify (G_OBJECT (object), "prop4");
     456    g_object_thaw_notify (G_OBJECT (object));
     457  
     458    g_object_unref (object);
     459  }
     460  
     461  /* Test g_object_class_find_property() for overridden properties */
     462  static void
     463  test_find_overridden (void)
     464  {
     465    GObjectClass *object_class;
     466  
     467    object_class = g_type_class_peek (DERIVED_TYPE_OBJECT);
     468  
     469    g_assert (g_object_class_find_property (object_class, "prop1") == inherited_spec1);
     470    g_assert (g_object_class_find_property (object_class, "prop2") == inherited_spec2);
     471    g_assert (g_object_class_find_property (object_class, "prop3") == inherited_spec3);
     472    g_assert (g_object_class_find_property (object_class, "prop4") == inherited_spec4);
     473  }
     474  
     475  /* Test g_object_class_list_properties() for overridden properties */
     476  static void
     477  test_list_overridden (void)
     478  {
     479    GObjectClass *object_class;
     480    GParamSpec **properties;
     481    guint n_properties;
     482  
     483    object_class = g_type_class_peek (DERIVED_TYPE_OBJECT);
     484  
     485    properties = g_object_class_list_properties (object_class, &n_properties);
     486    g_assert (n_properties == 4);
     487    assert_in_properties (inherited_spec1, properties, n_properties);
     488    assert_in_properties (inherited_spec2, properties, n_properties);
     489    assert_in_properties (inherited_spec3, properties, n_properties);
     490    assert_in_properties (inherited_spec4, properties, n_properties);
     491    g_free (properties);
     492  }
     493  
     494  /* Test g_object_interface_find_property() */
     495  static void
     496  test_find_interface (void)
     497  {
     498    TestIfaceClass *iface;
     499  
     500    iface = g_type_default_interface_peek (TEST_TYPE_IFACE);
     501  
     502    g_assert (g_object_interface_find_property (iface, "prop1") == iface_spec1);
     503    g_assert (g_object_interface_find_property (iface, "prop2") == iface_spec2);
     504    g_assert (g_object_interface_find_property (iface, "prop3") == iface_spec3);
     505  }
     506  
     507  /* Test g_object_interface_list_properties() */
     508  static void
     509  test_list_interface (void)
     510  {
     511    TestIfaceClass *iface;
     512    GParamSpec **properties;
     513    guint n_properties;
     514  
     515    iface = g_type_default_interface_peek (TEST_TYPE_IFACE);
     516  
     517    properties = g_object_interface_list_properties (iface, &n_properties);
     518    g_assert (n_properties == 3);
     519    assert_in_properties (iface_spec1, properties, n_properties);
     520    assert_in_properties (iface_spec2, properties, n_properties);
     521    assert_in_properties (iface_spec3, properties, n_properties);
     522    g_free (properties);
     523  }
     524  
     525  /* Base2Object, which implements the interface but fails
     526   * to override some of its properties
     527   */
     528  #define BASE2_TYPE_OBJECT          (base2_object_get_type ())
     529  #define BASE2_OBJECT(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE2_TYPE_OBJECT, Base2Object))
     530  
     531  typedef struct _Base2Object        Base2Object;
     532  typedef struct _Base2ObjectClass   Base2ObjectClass;
     533  
     534  static void
     535  base2_object_test_iface_init (TestIfaceClass *iface)
     536  {
     537  }
     538  
     539  enum {
     540    BASE2_PROP_0,
     541    BASE2_PROP1,
     542    BASE2_PROP2
     543  };
     544  
     545  struct _Base2Object
     546  {
     547    GObject parent_instance;
     548  };
     549  
     550  struct _Base2ObjectClass
     551  {
     552    GObjectClass parent_class;
     553  };
     554  
     555  static GType base2_object_get_type (void);
     556  G_DEFINE_TYPE_WITH_CODE (Base2Object, base2_object, G_TYPE_OBJECT,
     557                           G_IMPLEMENT_INTERFACE (TEST_TYPE_IFACE,
     558                                                  base2_object_test_iface_init))
     559  
     560  static void
     561  base2_object_get_property (GObject    *object,
     562                             guint       prop_id,
     563                             GValue     *value,
     564                             GParamSpec *pspec)
     565  {
     566    switch (prop_id)
     567      {
     568      case BASE2_PROP1:
     569        g_value_set_int (value, 0);
     570        break;
     571      case BASE2_PROP2:
     572        g_value_set_int (value, 0);
     573        break;
     574      default:
     575        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     576        break;
     577      }
     578  }
     579  
     580  static void
     581  base2_object_set_property (GObject      *object,
     582                             guint         prop_id,
     583                             const GValue *value,
     584                             GParamSpec   *pspec)
     585  {
     586    switch (prop_id)
     587      {
     588      case BASE2_PROP1:
     589        break;
     590      case BASE2_PROP2:
     591        break;
     592      default:
     593        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     594        break;
     595      }
     596  }
     597  
     598  static void
     599  base2_object_class_init (Base2ObjectClass *class)
     600  {
     601    GObjectClass *object_class = G_OBJECT_CLASS (class);
     602  
     603    object_class->set_property = base2_object_set_property;
     604    object_class->get_property = base2_object_get_property;
     605  
     606    g_object_class_override_property (object_class, BASE2_PROP1, "prop1");
     607    g_object_class_override_property (object_class, BASE2_PROP2, "prop2");
     608  }
     609  
     610  static void
     611  base2_object_init (Base2Object *object)
     612  {
     613  }
     614  
     615  static void
     616  test_not_overridden (void)
     617  {
     618    Base2Object *object;
     619  
     620    if (!g_test_undefined ())
     621      return;
     622  
     623    g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=637738");
     624  
     625    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     626                           "*Base2Object doesn't implement property 'prop3' from interface 'TestIface'*");
     627    object = g_object_new (BASE2_TYPE_OBJECT, NULL);
     628    g_test_assert_expected_messages ();
     629  
     630    g_object_unref (object);
     631  }
     632  
     633  int
     634  main (int argc, char *argv[])
     635  {
     636    g_test_init (&argc, &argv, NULL);
     637  
     638    g_test_add_func ("/interface/properties/set", test_set);
     639    g_test_add_func ("/interface/properties/notify", test_notify);
     640    g_test_add_func ("/interface/properties/find-overridden", test_find_overridden);
     641    g_test_add_func ("/interface/properties/list-overridden", test_list_overridden);
     642    g_test_add_func ("/interface/properties/find-interface", test_find_interface);
     643    g_test_add_func ("/interface/properties/list-interface", test_list_interface);
     644    g_test_add_func ("/interface/properties/not-overridden", test_not_overridden);
     645  
     646    return g_test_run ();
     647  }