Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright © 2011, 2014, 2024 Red Hat, Inc.
4 : : * Copyright © 2022 Peter Bloomfield
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General
19 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <glib.h>
23 : : #include <stdlib.h>
24 : :
25 : : #include "glib/glib-private.h"
26 : :
27 : : static void
28 : 1 : test_quark_basic (void)
29 : : {
30 : : GQuark quark;
31 : 1 : const gchar *orig = "blargh";
32 : : gchar *copy;
33 : : const gchar *str;
34 : :
35 : 1 : quark = g_quark_try_string ("no-such-quark");
36 : 1 : g_assert_cmpuint (quark, ==, 0);
37 : :
38 : 1 : copy = g_strdup (orig);
39 : 1 : quark = g_quark_from_static_string (orig);
40 : 1 : g_assert_cmpuint (quark, !=, 0);
41 : 1 : g_assert_cmpuint (g_quark_from_string (orig), ==, quark);
42 : 1 : g_assert_cmpuint (g_quark_from_string (copy), ==, quark);
43 : 1 : g_assert_cmpuint (g_quark_try_string (orig), ==, quark);
44 : :
45 : 1 : str = g_quark_to_string (quark);
46 : 1 : g_assert_cmpstr (str, ==, orig);
47 : :
48 : 1 : g_free (copy);
49 : 1 : }
50 : :
51 : : static void
52 : 1 : test_quark_string (void)
53 : : {
54 : 1 : const gchar *orig = "string1";
55 : : gchar *copy;
56 : : const gchar *str1;
57 : : const gchar *str2;
58 : :
59 : 1 : copy = g_strdup (orig);
60 : :
61 : 1 : str1 = g_intern_static_string (orig);
62 : 1 : str2 = g_intern_string (copy);
63 : 1 : g_assert_cmpstr (str1, ==, str2);
64 : 1 : g_assert_true (str1 == str2); /* also compare pointers */
65 : 1 : g_assert_cmpstr (str1, ==, orig);
66 : 1 : g_assert_true (str1 == orig);
67 : :
68 : 1 : g_free (copy);
69 : 1 : }
70 : :
71 : : static void
72 : 1 : test_dataset_basic (void)
73 : : {
74 : 1 : gpointer location = (gpointer)test_dataset_basic;
75 : 1 : gpointer other = (gpointer)test_quark_basic;
76 : 1 : gpointer data = "test1";
77 : : gpointer ret;
78 : :
79 : 1 : g_dataset_set_data (location, "test1", data);
80 : :
81 : 1 : ret = g_dataset_get_data (location, "test1");
82 : 1 : g_assert_true (ret == data);
83 : :
84 : 1 : ret = g_dataset_get_data (location, "test2");
85 : 1 : g_assert_null (ret);
86 : :
87 : 1 : ret = g_dataset_get_data (other, "test1");
88 : 1 : g_assert_null (ret);
89 : :
90 : 1 : g_dataset_set_data (location, "test1", "new-value");
91 : 1 : ret = g_dataset_get_data (location, "test1");
92 : 1 : g_assert_true (ret != data);
93 : :
94 : 1 : g_dataset_remove_data (location, "test1");
95 : 1 : ret = g_dataset_get_data (location, "test1");
96 : 1 : g_assert_null (ret);
97 : :
98 : 1 : ret = g_dataset_get_data (location, NULL);
99 : 1 : g_assert_null (ret);
100 : 1 : }
101 : :
102 : : static guint destroy_count;
103 : :
104 : : static void
105 : 11 : notify (gpointer data)
106 : : {
107 : 11 : destroy_count++;
108 : 11 : }
109 : :
110 : : static void
111 : 1 : test_dataset_full (void)
112 : : {
113 : 1 : gpointer location = (gpointer)test_dataset_full;
114 : :
115 : 1 : g_dataset_set_data_full (location, "test1", "test1", notify);
116 : :
117 : 1 : destroy_count = 0;
118 : 1 : g_dataset_set_data (location, "test1", NULL);
119 : 1 : g_assert_cmpuint (destroy_count, ==, 1);
120 : :
121 : 1 : g_dataset_set_data_full (location, "test1", "test1", notify);
122 : :
123 : 1 : destroy_count = 0;
124 : 1 : g_dataset_remove_data (location, "test1");
125 : 1 : g_assert_cmpuint (destroy_count, ==, 1);
126 : :
127 : 1 : g_dataset_set_data_full (location, "test1", "test1", notify);
128 : :
129 : 1 : destroy_count = 0;
130 : 1 : g_dataset_remove_no_notify (location, "test1");
131 : 1 : g_assert_cmpuint (destroy_count, ==, 0);
132 : 1 : }
133 : :
134 : : static void
135 : 3 : foreach (GQuark id,
136 : : gpointer data,
137 : : gpointer user_data)
138 : : {
139 : 3 : guint *counter = user_data;
140 : :
141 : 3 : *counter += 1;
142 : 3 : }
143 : :
144 : : static void
145 : 1 : test_dataset_foreach (void)
146 : : {
147 : 1 : gpointer location = (gpointer)test_dataset_foreach;
148 : : guint my_count;
149 : :
150 : 1 : my_count = 0;
151 : 1 : g_dataset_set_data_full (location, "test1", "test1", notify);
152 : 1 : g_dataset_set_data_full (location, "test2", "test2", notify);
153 : 1 : g_dataset_set_data_full (location, "test3", "test3", notify);
154 : 1 : g_dataset_foreach (location, foreach, &my_count);
155 : 1 : g_assert_cmpuint (my_count, ==, 3);
156 : :
157 : 1 : g_dataset_destroy (location);
158 : 1 : }
159 : :
160 : : static void
161 : 1 : test_dataset_destroy (void)
162 : : {
163 : 1 : gpointer location = (gpointer)test_dataset_destroy;
164 : :
165 : 1 : destroy_count = 0;
166 : 1 : g_dataset_set_data_full (location, "test1", "test1", notify);
167 : 1 : g_dataset_set_data_full (location, "test2", "test2", notify);
168 : 1 : g_dataset_set_data_full (location, "test3", "test3", notify);
169 : 1 : g_dataset_destroy (location);
170 : 1 : g_assert_cmpuint (destroy_count, ==, 3);
171 : 1 : }
172 : :
173 : : static void
174 : 1 : test_dataset_id (void)
175 : : {
176 : 1 : gpointer location = (gpointer)test_dataset_id;
177 : 1 : gpointer other = (gpointer)test_quark_basic;
178 : 1 : gpointer data = "test1";
179 : : gpointer ret;
180 : : GQuark quark;
181 : :
182 : 1 : quark = g_quark_from_string ("test1");
183 : :
184 : 1 : g_dataset_id_set_data (location, quark, data);
185 : :
186 : 1 : ret = g_dataset_id_get_data (location, quark);
187 : 1 : g_assert_true (ret == data);
188 : :
189 : 1 : ret = g_dataset_id_get_data (location, g_quark_from_string ("test2"));
190 : 1 : g_assert_null (ret);
191 : :
192 : 1 : ret = g_dataset_id_get_data (other, quark);
193 : 1 : g_assert_null (ret);
194 : :
195 : 1 : g_dataset_id_set_data (location, quark, "new-value");
196 : 1 : ret = g_dataset_id_get_data (location, quark);
197 : 1 : g_assert_true (ret != data);
198 : :
199 : 1 : g_dataset_id_remove_data (location, quark);
200 : 1 : ret = g_dataset_id_get_data (location, quark);
201 : 1 : g_assert_null (ret);
202 : :
203 : 1 : ret = g_dataset_id_get_data (location, 0);
204 : 1 : g_assert_null (ret);
205 : 1 : }
206 : :
207 : : static GData *global_list;
208 : :
209 : : static void
210 : 1 : free_one (gpointer data)
211 : : {
212 : : /* recurse */
213 : 1 : g_datalist_clear (&global_list);
214 : 1 : }
215 : :
216 : : static void
217 : 1 : test_datalist_clear (void)
218 : : {
219 : 1 : g_datalist_init (&global_list);
220 : 1 : g_datalist_set_data_full (&global_list, "one", GINT_TO_POINTER (1), free_one);
221 : 1 : g_datalist_set_data_full (&global_list, "two", GINT_TO_POINTER (2), NULL);
222 : 1 : g_datalist_clear (&global_list);
223 : 1 : g_assert_null (global_list);
224 : 1 : }
225 : :
226 : : static void
227 : 1 : test_datalist_basic (void)
228 : : {
229 : 1 : GData *list = NULL;
230 : : gpointer data;
231 : : gpointer ret;
232 : :
233 : 1 : g_datalist_init (&list);
234 : 1 : data = "one";
235 : 1 : g_datalist_set_data (&list, "one", data);
236 : 1 : ret = g_datalist_get_data (&list, "one");
237 : 1 : g_assert_true (ret == data);
238 : :
239 : 1 : ret = g_datalist_get_data (&list, "two");
240 : 1 : g_assert_null (ret);
241 : :
242 : 1 : ret = g_datalist_get_data (&list, NULL);
243 : 1 : g_assert_null (ret);
244 : :
245 : 1 : g_datalist_clear (&list);
246 : 1 : }
247 : :
248 : : static void
249 : 1 : test_datalist_id (void)
250 : : {
251 : 1 : GData *list = NULL;
252 : : gpointer data;
253 : : gpointer ret;
254 : :
255 : 1 : g_datalist_init (&list);
256 : 1 : data = "one";
257 : 1 : g_datalist_id_set_data (&list, g_quark_from_string ("one"), data);
258 : 1 : ret = g_datalist_id_get_data (&list, g_quark_from_string ("one"));
259 : 1 : g_assert_true (ret == data);
260 : :
261 : 1 : ret = g_datalist_id_get_data (&list, g_quark_from_string ("two"));
262 : 1 : g_assert_null (ret);
263 : :
264 : 1 : ret = g_datalist_id_get_data (&list, 0);
265 : 1 : g_assert_null (ret);
266 : :
267 : 1 : g_datalist_clear (&list);
268 : 1 : }
269 : :
270 : : static void
271 : 1 : test_datalist_id_remove_multiple (void)
272 : : {
273 : : /* Test that g_datalist_id_remove_multiple() removes all the keys it
274 : : * is given. */
275 : 1 : GData *list = NULL;
276 : 1 : GQuark one = g_quark_from_static_string ("one");
277 : 1 : GQuark two = g_quark_from_static_string ("two");
278 : 1 : GQuark three = g_quark_from_static_string ("three");
279 : 1 : GQuark keys[] = {
280 : : one,
281 : : two,
282 : : three,
283 : : };
284 : :
285 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2672");
286 : :
287 : 1 : g_datalist_init (&list);
288 : 1 : g_datalist_id_set_data (&list, one, GINT_TO_POINTER (1));
289 : 1 : g_datalist_id_set_data (&list, two, GINT_TO_POINTER (2));
290 : 1 : g_datalist_id_set_data (&list, three, GINT_TO_POINTER (3));
291 : :
292 : 1 : destroy_count = 0;
293 : 1 : g_datalist_foreach (&list, (GDataForeachFunc) notify, NULL);
294 : 1 : g_assert_cmpuint (destroy_count, ==, 3);
295 : :
296 : 1 : g_datalist_id_remove_multiple (&list, keys, G_N_ELEMENTS (keys));
297 : :
298 : 1 : destroy_count = 0;
299 : 1 : g_datalist_foreach (&list, (GDataForeachFunc) notify, NULL);
300 : 1 : g_assert_cmpuint (destroy_count, ==, 0);
301 : 1 : }
302 : :
303 : : static void
304 : 1 : test_datalist_id_remove_multiple_resize (void)
305 : : {
306 : : GQuark *quarks;
307 : : GQuark *quarks2;
308 : 1 : const guint N = 1000;
309 : 1 : const guint PRIME = 1048583u;
310 : : guint i;
311 : : char sbuf[100];
312 : 1 : GData *list = NULL;
313 : : guint i_run;
314 : :
315 : 1 : quarks = g_new (GQuark, N);
316 : 1 : quarks2 = g_new (GQuark, N);
317 : :
318 [ + + ]: 1001 : for (i = 0; i < N; i++)
319 : : {
320 : 1000 : g_snprintf (sbuf, sizeof (sbuf), "%d", i);
321 : 1000 : quarks[i] = g_quark_from_string (sbuf);
322 : : }
323 : :
324 [ + + ]: 1001 : for (i = 0; i < N; i++)
325 : 1000 : g_datalist_id_set_data (&list, quarks[i], GINT_TO_POINTER (i));
326 : :
327 : : /* Now we perform a list of random operations (remove/add quarks). */
328 : 1 : for (i_run = 0; TRUE; i_run++)
329 : 25 : {
330 : 26 : int MODE = ((guint) g_test_rand_int ()) % 4;
331 : : guint n;
332 : : guint j;
333 : :
334 : 26 : n = ((guint) g_test_rand_int ()) % (N + 1);
335 : 26 : j = ((guint) g_test_rand_int ()) % N;
336 : :
337 [ + + ]: 26 : if (i_run > 20)
338 : : {
339 : : /* After a few runs, we only remove elements, until the list
340 : : * is empty. */
341 [ + + ]: 5 : if (!list)
342 : 1 : break;
343 : 4 : MODE = 0;
344 [ - + ]: 4 : if (i_run > 30)
345 : 0 : n = N;
346 : : }
347 : :
348 [ + + - ]: 25 : switch (MODE)
349 : : {
350 : 23 : case 0:
351 : : case 1:
352 : : case 2:
353 : : /* Mode: add or remove a number of random quarks. */
354 [ + + ]: 10968 : for (i = 0; i < n; i++)
355 : : {
356 : 10945 : j = (j + PRIME) % N;
357 [ + + ]: 10945 : if (MODE == 0)
358 : 3403 : g_datalist_id_remove_data (&list, quarks[j]);
359 : : else
360 : 7542 : g_datalist_id_set_data (&list, quarks[j], GINT_TO_POINTER (j));
361 : : }
362 : 23 : break;
363 : 2 : case 3:
364 : : /* Mode: remove a list of (random) quarks. */
365 [ + + ]: 704 : for (i = 0; i < n; i++)
366 : : {
367 : 702 : j = (j + PRIME) % N;
368 : 702 : quarks2[i] = quarks[j];
369 : : }
370 : :
371 : 2 : g_datalist_id_remove_multiple (&list, quarks2, n);
372 : 2 : break;
373 : : }
374 : : }
375 : :
376 : 1 : g_free (quarks);
377 : 1 : g_free (quarks2);
378 : 1 : }
379 : :
380 : : static void
381 : 3 : destroy_func (gpointer data)
382 : : {
383 : 3 : destroy_count++;
384 : 3 : g_assert_cmpuint (GPOINTER_TO_UINT (data), ==, destroy_count);
385 : 3 : }
386 : :
387 : : static void
388 : 1 : test_datalist_id_remove_multiple_destroy_order (void)
389 : : {
390 : : /* Test that destroy-funcs are called in the order that the keys are
391 : : * specified, not the order that they are found in the datalist. */
392 : 1 : GData *list = NULL;
393 : 1 : GQuark one = g_quark_from_static_string ("one");
394 : 1 : GQuark two = g_quark_from_static_string ("two");
395 : 1 : GQuark three = g_quark_from_static_string ("three");
396 : 1 : GQuark keys[] = {
397 : : one,
398 : : two,
399 : : three,
400 : : };
401 : :
402 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2672");
403 : :
404 : 1 : g_datalist_init (&list);
405 : :
406 : 1 : g_datalist_id_set_data_full (&list, two, GUINT_TO_POINTER (2), destroy_func);
407 : 1 : g_datalist_id_set_data_full (&list, three, GUINT_TO_POINTER (3), destroy_func);
408 : 1 : g_datalist_id_set_data_full (&list, one, GUINT_TO_POINTER (1), destroy_func);
409 : :
410 : 1 : destroy_count = 0;
411 : 1 : g_datalist_id_remove_multiple (&list, keys, G_N_ELEMENTS (keys));
412 : : /* This verifies that destroy_func() was called three times: */
413 : 1 : g_assert_cmpuint (destroy_count, ==, 3);
414 : 1 : }
415 : :
416 : : static gpointer
417 : 2 : _update_atomic_cb (GQuark key_id,
418 : : gpointer *data,
419 : : GDestroyNotify *destroy_notify,
420 : : gpointer user_data)
421 : : {
422 : 2 : const char *op = user_data;
423 : 2 : char *data_entry = *data;
424 : :
425 : 2 : g_assert_nonnull (op);
426 : :
427 [ + + ]: 2 : if (strcmp (op, "create") == 0)
428 : : {
429 : 1 : g_assert_cmpstr (data_entry, ==, NULL);
430 : 1 : g_assert_null (*destroy_notify);
431 : :
432 : 1 : *data = g_strdup ("hello");
433 : 1 : *destroy_notify = g_free;
434 : : }
435 [ + - ]: 1 : else if (strcmp (op, "remove") == 0)
436 : : {
437 : 1 : g_assert_cmpstr (data_entry, ==, "hello");
438 : 1 : g_assert_true (*destroy_notify == g_free);
439 : :
440 : 1 : g_free (data_entry);
441 : 1 : *data = NULL;
442 : : }
443 : : else
444 : : g_assert_not_reached ();
445 : :
446 : 2 : return "result";
447 : : }
448 : :
449 : : static void
450 : 1 : test_datalist_update_atomic (void)
451 : : {
452 : 1 : GQuark one = g_quark_from_static_string ("one");
453 : 1 : GData *list = NULL;
454 : : const char *result;
455 : :
456 : 1 : result = _g_datalist_id_update_atomic (&list, one, _update_atomic_cb, "create");
457 : 1 : g_assert_cmpstr (result, ==, "result");
458 : 1 : g_assert_cmpstr ((const char *) g_datalist_id_get_data (&list, one), ==, "hello");
459 : :
460 : 1 : g_datalist_id_set_data_full (&list, one, g_strdup ("hello"), g_free);
461 : :
462 : 1 : result = _g_datalist_id_update_atomic (&list, one, _update_atomic_cb, "remove");
463 : 1 : g_assert_cmpstr (result, ==, "result");
464 : :
465 : 1 : g_assert_null (list);
466 : 1 : }
467 : :
468 : : int
469 : 1 : main (int argc, char *argv[])
470 : : {
471 : 1 : g_test_init (&argc, &argv, NULL);
472 : :
473 : 1 : g_test_add_func ("/quark/basic", test_quark_basic);
474 : 1 : g_test_add_func ("/quark/string", test_quark_string);
475 : 1 : g_test_add_func ("/dataset/basic", test_dataset_basic);
476 : 1 : g_test_add_func ("/dataset/id", test_dataset_id);
477 : 1 : g_test_add_func ("/dataset/full", test_dataset_full);
478 : 1 : g_test_add_func ("/dataset/foreach", test_dataset_foreach);
479 : 1 : g_test_add_func ("/dataset/destroy", test_dataset_destroy);
480 : 1 : g_test_add_func ("/datalist/basic", test_datalist_basic);
481 : 1 : g_test_add_func ("/datalist/id", test_datalist_id);
482 : 1 : g_test_add_func ("/datalist/recursive-clear", test_datalist_clear);
483 : 1 : g_test_add_func ("/datalist/id-remove-multiple", test_datalist_id_remove_multiple);
484 : 1 : g_test_add_func ("/datalist/id-remove-multiple/resize", test_datalist_id_remove_multiple_resize);
485 : 1 : g_test_add_func ("/datalist/id-remove-multiple-destroy-order",
486 : : test_datalist_id_remove_multiple_destroy_order);
487 : 1 : g_test_add_func ("/datalist/update-atomic", test_datalist_update_atomic);
488 : :
489 : 1 : return g_test_run ();
490 : : }
|