1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21 * Modified by the GLib Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GLib Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GLib at ftp://ftp.gtk.org/pub/gtk/.
25 */
26
27 #undef G_DISABLE_ASSERT
28 #undef G_LOG_DOMAIN
29
30 /* We are testing some deprecated APIs here */
31 #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
32 #define GLIB_DISABLE_DEPRECATION_WARNINGS
33 #endif
34
35 #include <stdio.h>
36 #include <string.h>
37 #include "glib.h"
38
39
40 static gint
41 my_compare (gconstpointer a,
42 gconstpointer b)
43 {
44 const char *cha = a;
45 const char *chb = b;
46
47 return *cha - *chb;
48 }
49
50 static gint
51 my_compare_with_data (gconstpointer a,
52 gconstpointer b,
53 gpointer user_data)
54 {
55 const char *cha = a;
56 const char *chb = b;
57
58 /* just check that we got the right data */
59 g_assert_cmpint (GPOINTER_TO_INT (user_data), ==, 123);
60
61 return *cha - *chb;
62 }
63
64 static gint
65 my_search (gconstpointer a,
66 gconstpointer b)
67 {
68 return my_compare (b, a);
69 }
70
71 static gpointer destroyed_key = NULL;
72 static gpointer destroyed_value = NULL;
73 static guint destroyed_key_count = 0;
74 static guint destroyed_value_count = 0;
75
76 static void
77 my_key_destroy (gpointer key)
78 {
79 destroyed_key = key;
80 destroyed_key_count++;
81 }
82
83 static void
84 my_value_destroy (gpointer value)
85 {
86 destroyed_value = value;
87 destroyed_value_count++;
88 }
89
90 static gint
91 my_traverse (gpointer key,
92 gpointer value,
93 gpointer data)
94 {
95 char *ch = key;
96
97 g_assert_cmpint ((*ch), >, 0);
98
99 if (*ch == 'd')
100 return TRUE;
101
102 return FALSE;
103 }
104
105 char chars[] =
106 "0123456789"
107 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
108 "abcdefghijklmnopqrstuvwxyz";
109
110 char chars2[] =
111 "0123456789"
112 "abcdefghijklmnopqrstuvwxyz";
113
114 static gint
115 check_order (gpointer key,
116 gpointer value,
117 gpointer data)
118 {
119 char **p = data;
120 char *ch = key;
121
122 g_assert_cmpint (**p, ==, *ch);
123
124 (*p)++;
125
126 return FALSE;
127 }
128
129 static void
130 test_tree_search (void)
131 {
132 gint i;
133 GTree *tree;
134 gboolean removed;
135 gchar c;
136 gchar *p, *d;
137
138 tree = g_tree_new_with_data (my_compare_with_data, GINT_TO_POINTER(123));
139
140 for (i = 0; chars[i]; i++)
141 g_tree_insert (tree, &chars[i], &chars[i]);
142
143 g_tree_foreach (tree, my_traverse, NULL);
144
145 g_assert_cmpint (g_tree_nnodes (tree), ==, strlen (chars));
146 g_assert_cmpint (g_tree_height (tree), ==, 6);
147
148 p = chars;
149 g_tree_foreach (tree, check_order, &p);
150
151 for (i = 0; i < 26; i++)
152 {
153 removed = g_tree_remove (tree, &chars[i + 10]);
154 g_assert_true (removed);
155 }
156
157 c = '\0';
158 removed = g_tree_remove (tree, &c);
159 g_assert_false (removed);
160
161 g_tree_foreach (tree, my_traverse, NULL);
162
163 g_assert_cmpint (g_tree_nnodes (tree), ==, strlen (chars2));
164 g_assert_cmpint (g_tree_height (tree), ==, 6);
165
166 p = chars2;
167 g_tree_foreach (tree, check_order, &p);
168
169 for (i = 25; i >= 0; i--)
170 g_tree_insert (tree, &chars[i + 10], &chars[i + 10]);
171
172 p = chars;
173 g_tree_foreach (tree, check_order, &p);
174
175 c = '0';
176 p = g_tree_lookup (tree, &c);
177 g_assert_true (p && *p == c);
178 g_assert_true (g_tree_lookup_extended (tree, &c, (gpointer *)&d, (gpointer *)&p));
179 g_assert_true (c == *d && c == *p);
180
181 c = 'A';
182 p = g_tree_lookup (tree, &c);
183 g_assert_true (p && *p == c);
184
185 c = 'a';
186 p = g_tree_lookup (tree, &c);
187 g_assert_true (p && *p == c);
188
189 c = 'z';
190 p = g_tree_lookup (tree, &c);
191 g_assert_true (p && *p == c);
192
193 c = '!';
194 p = g_tree_lookup (tree, &c);
195 g_assert_null (p);
196
197 c = '=';
198 p = g_tree_lookup (tree, &c);
199 g_assert_null (p);
200
201 c = '|';
202 p = g_tree_lookup (tree, &c);
203 g_assert_null (p);
204
205 c = '0';
206 p = g_tree_search (tree, my_search, &c);
207 g_assert_true (p && *p == c);
208
209 c = 'A';
210 p = g_tree_search (tree, my_search, &c);
211 g_assert_true (p && *p == c);
212
213 c = 'a';
214 p = g_tree_search (tree, my_search, &c);
215 g_assert_true (p &&*p == c);
216
217 c = 'z';
218 p = g_tree_search (tree, my_search, &c);
219 g_assert_true (p && *p == c);
220
221 c = '!';
222 p = g_tree_search (tree, my_search, &c);
223 g_assert_null (p);
224
225 c = '=';
226 p = g_tree_search (tree, my_search, &c);
227 g_assert_null (p);
228
229 c = '|';
230 p = g_tree_search (tree, my_search, &c);
231 g_assert_null (p);
232
233 g_tree_destroy (tree);
234 }
235
236 static void
237 test_tree_remove (void)
238 {
239 GTree *tree;
240 char c, d, e, f;
241 gint i;
242 gboolean removed;
243 GTreeNode *node;
244 gchar *remove;
245
246 tree = g_tree_new_full ((GCompareDataFunc)my_compare, NULL,
247 my_key_destroy,
248 my_value_destroy);
249
250 for (i = 0; chars[i]; i++)
251 g_tree_insert (tree, &chars[i], &chars[i]);
252
253 c = '0';
254 g_tree_insert (tree, &c, &c);
255 g_assert_true (destroyed_key == &c);
256 g_assert_true (destroyed_value == &chars[0]);
257 destroyed_key = NULL;
258 destroyed_value = NULL;
259
260 d = '1';
261 g_tree_replace (tree, &d, &d);
262 g_assert_true (destroyed_key == &chars[1]);
263 g_assert_true (destroyed_value == &chars[1]);
264 destroyed_key = NULL;
265 destroyed_value = NULL;
266
267 e = '\xff';
268 node = g_tree_insert_node (tree, &e, &e);
269 g_assert_nonnull (node);
270 g_assert_null (destroyed_key);
271 g_assert_null (destroyed_value);
272
273 c = '2';
274 removed = g_tree_remove (tree, &c);
275 g_assert_true (removed);
276 g_assert_true (destroyed_key == &chars[2]);
277 g_assert_true (destroyed_value == &chars[2]);
278 destroyed_key = NULL;
279 destroyed_value = NULL;
280
281 c = '3';
282 removed = g_tree_steal (tree, &c);
283 g_assert_true (removed);
284 g_assert_null (destroyed_key);
285 g_assert_null (destroyed_value);
286
287 f = '4';
288 node = g_tree_replace_node (tree, &f, &f);
289 g_assert_nonnull (node);
290 g_assert_true (destroyed_key == &chars[4]);
291 g_assert_true (destroyed_value == &chars[4]);
292 destroyed_key = NULL;
293 destroyed_value = NULL;
294
295 remove = "omkjigfedba";
296 for (i = 0; remove[i]; i++)
297 {
298 removed = g_tree_remove (tree, &remove[i]);
299 g_assert_true (removed);
300 }
301
302 g_tree_destroy (tree);
303 }
304
305 static void
306 test_tree_remove_all (void)
307 {
308 GTree *tree;
309 gsize i;
310
311 tree = g_tree_new_full ((GCompareDataFunc)my_compare, NULL,
312 my_key_destroy,
313 my_value_destroy);
314
315 for (i = 0; chars[i]; i++)
316 g_tree_insert (tree, &chars[i], &chars[i]);
317
318 destroyed_key_count = 0;
319 destroyed_value_count = 0;
320
321 g_tree_remove_all (tree);
322
323 g_assert_cmpuint (destroyed_key_count, ==, strlen (chars));
324 g_assert_cmpuint (destroyed_value_count, ==, strlen (chars));
325 g_assert_cmpint (g_tree_height (tree), ==, 0);
326 g_assert_cmpint (g_tree_nnodes (tree), ==, 0);
327
328 g_tree_unref (tree);
329 }
330
331 static void
332 test_tree_destroy (void)
333 {
334 GTree *tree;
335 gint i;
336
337 tree = g_tree_new (my_compare);
338
339 for (i = 0; chars[i]; i++)
340 g_tree_insert (tree, &chars[i], &chars[i]);
341
342 g_assert_cmpint (g_tree_nnodes (tree), ==, strlen (chars));
343
344 g_tree_ref (tree);
345 g_tree_destroy (tree);
346
347 g_assert_cmpint (g_tree_nnodes (tree), ==, 0);
348
349 g_tree_unref (tree);
350 }
351
352 typedef struct {
353 GString *s;
354 gint count;
355 } CallbackData;
356
357 static gboolean
358 traverse_func (gpointer key, gpointer value, gpointer data)
359 {
360 CallbackData *d = data;
361
362 gchar *c = value;
363 g_string_append_c (d->s, *c);
364
365 d->count--;
366
367 if (d->count == 0)
368 return TRUE;
369
370 return FALSE;
371 }
372
373 typedef struct {
374 GTraverseType traverse;
375 gint limit;
376 const gchar *expected;
377 } TraverseData;
378
379 static void
380 test_tree_traverse (void)
381 {
382 GTree *tree;
383 gsize i;
384 TraverseData orders[] = {
385 { G_IN_ORDER, -1, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" },
386 { G_IN_ORDER, 1, "0" },
387 { G_IN_ORDER, 2, "01" },
388 { G_IN_ORDER, 3, "012" },
389 { G_IN_ORDER, 4, "0123" },
390 { G_IN_ORDER, 5, "01234" },
391 { G_IN_ORDER, 6, "012345" },
392 { G_IN_ORDER, 7, "0123456" },
393 { G_IN_ORDER, 8, "01234567" },
394 { G_IN_ORDER, 9, "012345678" },
395 { G_IN_ORDER, 10, "0123456789" },
396 { G_IN_ORDER, 11, "0123456789A" },
397 { G_IN_ORDER, 12, "0123456789AB" },
398 { G_IN_ORDER, 13, "0123456789ABC" },
399 { G_IN_ORDER, 14, "0123456789ABCD" },
400
401 { G_PRE_ORDER, -1, "VF73102546B98ADCENJHGILKMRPOQTSUldZXWYbachfegjiktpnmorqsxvuwyz" },
402 { G_PRE_ORDER, 1, "V" },
403 { G_PRE_ORDER, 2, "VF" },
404 { G_PRE_ORDER, 3, "VF7" },
405 { G_PRE_ORDER, 4, "VF73" },
406 { G_PRE_ORDER, 5, "VF731" },
407 { G_PRE_ORDER, 6, "VF7310" },
408 { G_PRE_ORDER, 7, "VF73102" },
409 { G_PRE_ORDER, 8, "VF731025" },
410 { G_PRE_ORDER, 9, "VF7310254" },
411 { G_PRE_ORDER, 10, "VF73102546" },
412 { G_PRE_ORDER, 11, "VF73102546B" },
413 { G_PRE_ORDER, 12, "VF73102546B9" },
414 { G_PRE_ORDER, 13, "VF73102546B98" },
415 { G_PRE_ORDER, 14, "VF73102546B98A" },
416
417 { G_POST_ORDER, -1, "02146538A9CEDB7GIHKMLJOQPSUTRNFWYXacbZegfikjhdmonqsrpuwvzyxtlV" },
418 { G_POST_ORDER, 1, "0" },
419 { G_POST_ORDER, 2, "02" },
420 { G_POST_ORDER, 3, "021" },
421 { G_POST_ORDER, 4, "0214" },
422 { G_POST_ORDER, 5, "02146" },
423 { G_POST_ORDER, 6, "021465" },
424 { G_POST_ORDER, 7, "0214653" },
425 { G_POST_ORDER, 8, "02146538" },
426 { G_POST_ORDER, 9, "02146538A" },
427 { G_POST_ORDER, 10, "02146538A9" },
428 { G_POST_ORDER, 11, "02146538A9C" },
429 { G_POST_ORDER, 12, "02146538A9CE" },
430 { G_POST_ORDER, 13, "02146538A9CED" },
431 { G_POST_ORDER, 14, "02146538A9CEDB" }
432 };
433 CallbackData data;
434
435 tree = g_tree_new (my_compare);
436
437 for (i = 0; chars[i]; i++)
438 g_tree_insert (tree, &chars[i], &chars[i]);
439
440 data.s = g_string_new ("");
441 for (i = 0; i < G_N_ELEMENTS (orders); i++)
442 {
443 g_string_set_size (data.s, 0);
444 data.count = orders[i].limit;
445 g_tree_traverse (tree, traverse_func, orders[i].traverse, &data);
446 g_assert_cmpstr (data.s->str, ==, orders[i].expected);
447 }
448
449 g_tree_unref (tree);
450 g_string_free (data.s, TRUE);
451 }
452
453 static void
454 test_tree_insert (void)
455 {
456 GTree *tree;
457 gchar *p;
458 gint i;
459 gchar *scrambled;
460
461 tree = g_tree_new (my_compare);
462
463 for (i = 0; chars[i]; i++)
464 g_tree_insert (tree, &chars[i], &chars[i]);
465 p = chars;
466 g_tree_foreach (tree, check_order, &p);
467
468 g_tree_unref (tree);
469 tree = g_tree_new (my_compare);
470
471 for (i = strlen (chars) - 1; i >= 0; i--)
472 g_tree_insert (tree, &chars[i], &chars[i]);
473 p = chars;
474 g_tree_foreach (tree, check_order, &p);
475
476 g_tree_unref (tree);
477 tree = g_tree_new (my_compare);
478
479 scrambled = g_strdup (chars);
480
481 for (i = 0; i < 30; i++)
482 {
483 gchar tmp;
484 gint a, b;
485
486 a = g_random_int_range (0, strlen (scrambled));
487 b = g_random_int_range (0, strlen (scrambled));
488 tmp = scrambled[a];
489 scrambled[a] = scrambled[b];
490 scrambled[b] = tmp;
491 }
492
493 for (i = 0; scrambled[i]; i++)
494 g_tree_insert (tree, &scrambled[i], &scrambled[i]);
495 p = chars;
496 g_tree_foreach (tree, check_order, &p);
497
498 g_free (scrambled);
499 g_tree_unref (tree);
500 }
501
502 static void
503 binary_tree_bound (GTree *tree,
504 char c,
505 char expected,
506 int lower)
507 {
508 GTreeNode *node;
509
510 if (lower)
511 node = g_tree_lower_bound (tree, &c);
512 else
513 node = g_tree_upper_bound (tree, &c);
514
515 if (g_test_verbose ())
516 g_test_message ("%c %s: ", c, lower ? "lower" : "upper");
517
518 if (!node)
519 {
520 if (!g_tree_nnodes (tree))
521 {
522 if (g_test_verbose ())
523 g_test_message ("empty tree");
524 }
525 else
526 {
527 GTreeNode *last = g_tree_node_last (tree);
528
529 g_assert_nonnull (last);
530 if (g_test_verbose ())
531 g_test_message ("past end last %c",
532 *(char *) g_tree_node_key (last));
533 }
534 g_assert_cmpint (expected, ==, '\x00');
535 }
536 else
537 {
538 GTreeNode *begin = g_tree_node_first (tree);
539 GTreeNode *last = g_tree_node_last (tree);
540 GTreeNode *prev = g_tree_node_previous (node);
541 GTreeNode *next = g_tree_node_next (node);
542
543 g_assert_cmpint (expected, !=, '\x00');
544 g_assert_cmpint (expected, ==, *(char *) g_tree_node_key (node));
545
546 if (g_test_verbose ())
547 g_test_message ("%c", *(char *) g_tree_node_key (node));
548
549 if (node != begin)
550 {
551 g_assert_nonnull (prev);
552 if (g_test_verbose ())
553 g_test_message (" prev %c", *(char *) g_tree_node_key (prev));
554 }
555 else
556 {
557 g_assert_null (prev);
558 if (g_test_verbose ())
559 g_test_message (" no prev, it's the first one");
560 }
561
562 if (node != last)
563 {
564 g_assert_nonnull (next);
565 if (g_test_verbose ())
566 g_test_message (" next %c", *(char *) g_tree_node_key (next));
567 }
568 else
569 {
570 g_assert_null (next);
571 if (g_test_verbose ())
572 g_test_message (" no next, it's the last one");
573 }
574 }
575
576 if (g_test_verbose ())
577 g_test_message ("\n");
578 }
579
580 static void
581 binary_tree_bounds (GTree *tree,
582 char c,
583 int mode)
584 {
585 char expectedl, expectedu;
586 char first = mode == 0 ? '0' : mode == 1 ? 'A' : 'z';
587
588 g_assert_true (mode >= 0 && mode <= 3);
589
590 if (c < first)
591 expectedl = first;
592 else if (c > 'z')
593 expectedl = '\x00';
594 else
595 expectedl = c;
596
597 if (c < first)
598 expectedu = first;
599 else if (c >= 'z')
600 expectedu = '\x00';
601 else
602 expectedu = c == '9' ? 'A' : c == 'Z' ? 'a' : c + 1;
603
604 if (mode == 3)
605 {
606 expectedl = '\x00';
607 expectedu = '\x00';
608 }
609
610 binary_tree_bound (tree, c, expectedl, 1);
611 binary_tree_bound (tree, c, expectedu, 0);
612 }
613
614 static void
615 binary_tree_bounds_test (GTree *tree,
616 int mode)
617 {
618 binary_tree_bounds (tree, 'a', mode);
619 binary_tree_bounds (tree, 'A', mode);
620 binary_tree_bounds (tree, 'z', mode);
621 binary_tree_bounds (tree, 'Z', mode);
622 binary_tree_bounds (tree, 'Y', mode);
623 binary_tree_bounds (tree, '0', mode);
624 binary_tree_bounds (tree, '9', mode);
625 binary_tree_bounds (tree, '0' - 1, mode);
626 binary_tree_bounds (tree, 'z' + 1, mode);
627 binary_tree_bounds (tree, '0' - 2, mode);
628 binary_tree_bounds (tree, 'z' + 2, mode);
629 }
630
631 static void
632 test_tree_bounds (void)
633 {
634 GQueue queue = G_QUEUE_INIT;
635 GTree *tree;
636 char chars[62];
637 guint i, j;
638
639 tree = g_tree_new (my_compare);
640
641 i = 0;
642 for (j = 0; j < 10; j++, i++)
643 {
644 chars[i] = '0' + j;
645 g_queue_push_tail (&queue, &chars[i]);
646 }
647
648 for (j = 0; j < 26; j++, i++)
649 {
650 chars[i] = 'A' + j;
651 g_queue_push_tail (&queue, &chars[i]);
652 }
653
654 for (j = 0; j < 26; j++, i++)
655 {
656 chars[i] = 'a' + j;
657 g_queue_push_tail (&queue, &chars[i]);
658 }
659
660 if (g_test_verbose ())
661 g_test_message ("tree insert: ");
662
663 while (!g_queue_is_empty (&queue))
664 {
665 gint32 which = g_random_int_range (0, g_queue_get_length (&queue));
666 gpointer elem = g_queue_pop_nth (&queue, which);
667 GTreeNode *node;
668
669 if (g_test_verbose ())
670 g_test_message ("%c ", *(char *) elem);
671
672 node = g_tree_insert_node (tree, elem, elem);
673 g_assert_nonnull (node);
674 g_assert_true (g_tree_node_key (node) == elem);
675 g_assert_true (g_tree_node_value (node) == elem);
676 }
677
678 if (g_test_verbose ())
679 g_test_message ("\n");
680
681 g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
682 g_assert_cmpint (g_tree_height (tree), >=, 6);
683 g_assert_cmpint (g_tree_height (tree), <=, 8);
684
685 if (g_test_verbose ())
686 {
687 g_test_message ("tree: ");
688 g_tree_foreach (tree, my_traverse, NULL);
689 g_test_message ("\n");
690 }
691
692 binary_tree_bounds_test (tree, 0);
693
694 for (i = 0; i < 10; i++)
695 g_tree_remove (tree, &chars[i]);
696
697 g_assert_cmpint (g_tree_nnodes (tree), ==, 26 + 26);
698 g_assert_cmpint (g_tree_height (tree), >=, 6);
699 g_assert_cmpint (g_tree_height (tree), <=, 8);
700
701 if (g_test_verbose ())
702 {
703 g_test_message ("tree: ");
704 g_tree_foreach (tree, my_traverse, NULL);
705 g_test_message ("\n");
706 }
707
708 binary_tree_bounds_test (tree, 1);
709
710 for (i = 10; i < 10 + 26 + 26 - 1; i++)
711 g_tree_remove (tree, &chars[i]);
712
713 if (g_test_verbose ())
714 {
715 g_test_message ("tree: ");
716 g_tree_foreach (tree, my_traverse, NULL);
717 g_test_message ("\n");
718 }
719
720 binary_tree_bounds_test (tree, 2);
721
722 g_tree_remove (tree, &chars[10 + 26 + 26 - 1]);
723
724 if (g_test_verbose ())
725 g_test_message ("empty tree\n");
726
727 binary_tree_bounds_test (tree, 3);
728
729 g_tree_unref (tree);
730 }
731
732 int
733 main (int argc, char *argv[])
734 {
735 g_test_init (&argc, &argv, NULL);
736
737 g_test_add_func ("/tree/search", test_tree_search);
738 g_test_add_func ("/tree/remove", test_tree_remove);
739 g_test_add_func ("/tree/destroy", test_tree_destroy);
740 g_test_add_func ("/tree/traverse", test_tree_traverse);
741 g_test_add_func ("/tree/insert", test_tree_insert);
742 g_test_add_func ("/tree/bounds", test_tree_bounds);
743 g_test_add_func ("/tree/remove-all", test_tree_remove_all);
744
745 return g_test_run ();
746 }