Branch data Line data Source code
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 : 3 : G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
52 : :
53 : : static void
54 : 0 : test_object_set_foo (TestObject *obj,
55 : : gint foo)
56 : : {
57 : 0 : if (obj->foo != foo)
58 : : {
59 : 0 : obj->foo = foo;
60 : :
61 : 0 : g_assert (properties[PROP_FOO] != NULL);
62 : 0 : g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
63 : : }
64 : 0 : }
65 : :
66 : : static void
67 : 0 : test_object_set_bar (TestObject *obj,
68 : : gboolean bar)
69 : : {
70 : 0 : bar = !!bar;
71 : :
72 : 0 : if (obj->bar != bar)
73 : : {
74 : 0 : obj->bar = bar;
75 : :
76 : 0 : g_assert (properties[PROP_BAR] != NULL);
77 : 0 : g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
78 : : }
79 : 0 : }
80 : :
81 : : static void
82 : 0 : test_object_set_baz (TestObject *obj,
83 : : const gchar *baz)
84 : : {
85 : 0 : if (g_strcmp0 (obj->baz, baz) != 0)
86 : : {
87 : 0 : g_free (obj->baz);
88 : 0 : obj->baz = g_strdup (baz);
89 : :
90 : 0 : g_assert (properties[PROP_BAZ] != NULL);
91 : 0 : g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
92 : : }
93 : 0 : }
94 : :
95 : : static void
96 : 1 : test_object_set_quux (TestObject *obj,
97 : : const gchar *quux)
98 : : {
99 : 1 : if (g_strcmp0 (obj->quux, quux) != 0)
100 : : {
101 : 1 : g_free (obj->quux);
102 : 1 : obj->quux = g_strdup (quux);
103 : :
104 : 1 : g_assert (properties[PROP_QUUX] != NULL);
105 : 1 : g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_QUUX]);
106 : : }
107 : 1 : }
108 : :
109 : : static void
110 : 1 : test_object_finalize (GObject *gobject)
111 : : {
112 : 1 : TestObject *self = (TestObject *) gobject;
113 : :
114 : 1 : g_free (self->baz);
115 : 1 : g_free (self->quux);
116 : :
117 : 1 : G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
118 : 1 : }
119 : :
120 : : static void
121 : 0 : test_object_set_property (GObject *gobject,
122 : : guint prop_id,
123 : : const GValue *value,
124 : : GParamSpec *pspec)
125 : : {
126 : 0 : TestObject *tobj = (TestObject *) gobject;
127 : :
128 : 0 : g_assert_cmpint (prop_id, !=, 0);
129 : 0 : g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
130 : :
131 : 0 : switch ((TestObjectProperty)prop_id)
132 : : {
133 : 0 : case PROP_FOO:
134 : 0 : test_object_set_foo (tobj, g_value_get_int (value));
135 : 0 : break;
136 : :
137 : 0 : case PROP_BAR:
138 : 0 : test_object_set_bar (tobj, g_value_get_boolean (value));
139 : 0 : break;
140 : :
141 : 0 : case PROP_BAZ:
142 : 0 : test_object_set_baz (tobj, g_value_get_string (value));
143 : 0 : break;
144 : :
145 : 0 : case PROP_QUUX:
146 : 0 : test_object_set_quux (tobj, g_value_get_string (value));
147 : 0 : break;
148 : :
149 : 0 : default:
150 : : g_assert_not_reached ();
151 : : }
152 : 0 : }
153 : :
154 : : static void
155 : 0 : test_object_get_property (GObject *gobject,
156 : : guint prop_id,
157 : : GValue *value,
158 : : GParamSpec *pspec)
159 : : {
160 : 0 : TestObject *tobj = (TestObject *) gobject;
161 : :
162 : 0 : g_assert_cmpint (prop_id, !=, 0);
163 : 0 : g_assert_true (prop_id < N_PROPERTIES && pspec == properties[prop_id]);
164 : :
165 : 0 : switch ((TestObjectProperty)prop_id)
166 : : {
167 : 0 : case PROP_FOO:
168 : 0 : g_value_set_int (value, tobj->foo);
169 : 0 : break;
170 : :
171 : 0 : case PROP_BAR:
172 : 0 : g_value_set_boolean (value, tobj->bar);
173 : 0 : break;
174 : :
175 : 0 : case PROP_BAZ:
176 : 0 : g_value_set_string (value, tobj->baz);
177 : 0 : break;
178 : :
179 : 0 : case PROP_QUUX:
180 : 0 : g_value_set_string (value, tobj->quux);
181 : 0 : break;
182 : :
183 : 0 : default:
184 : : g_assert_not_reached ();
185 : : }
186 : 0 : }
187 : :
188 : : static void
189 : 1 : test_object_class_init (TestObjectClass *klass)
190 : : {
191 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
192 : :
193 : 1 : 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 : 1 : properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
198 : : FALSE,
199 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
200 : 1 : properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
201 : : NULL,
202 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
203 : :
204 : 1 : 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 : 1 : gobject_class->set_property = test_object_set_property;
209 : 1 : gobject_class->get_property = test_object_get_property;
210 : 1 : gobject_class->finalize = test_object_finalize;
211 : :
212 : 1 : g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
213 : 1 : }
214 : :
215 : : static void
216 : 1 : quux_changed (TestObject *self,
217 : : GParamSpec *pspec,
218 : : gpointer data)
219 : : {
220 : 1 : g_assert (self->baz != NULL);
221 : 1 : }
222 : :
223 : : static void
224 : 1 : 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 : 1 : g_signal_connect (self, "notify::quux", G_CALLBACK (quux_changed), NULL);
239 : :
240 : 1 : test_object_set_quux (self, "quux");
241 : :
242 : 1 : self->foo = 42;
243 : 1 : self->bar = TRUE;
244 : 1 : self->baz = g_strdup ("Hello");
245 : 1 : }
246 : :
247 : : static void
248 : 1 : test_notify_in_init (void)
249 : : {
250 : : TestObject *obj;
251 : :
252 : 1 : g_test_summary ("Test that emitting notify with a handler already connected in test_object_init() works");
253 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2665");
254 : :
255 : 1 : obj = g_object_new (test_object_get_type (), NULL);
256 : :
257 : 1 : g_object_unref (obj);
258 : 1 : }
259 : :
260 : : int
261 : 1 : main (int argc, char *argv[])
262 : : {
263 : 1 : g_test_init (&argc, &argv, NULL);
264 : :
265 : 1 : g_test_add_func ("/properties/notify-in-init", test_notify_in_init);
266 : :
267 : 1 : return g_test_run ();
268 : : }
|