| /* |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| * |
| * Tests for QTree. |
| * Original source: glib |
| * https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/tests/tree.c |
| * LGPL license. |
| * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/qtree.h" |
| |
| static gint my_compare(gconstpointer a, gconstpointer b) |
| { |
| const char *cha = a; |
| const char *chb = b; |
| |
| return *cha - *chb; |
| } |
| |
| static gint my_compare_with_data(gconstpointer a, |
| gconstpointer b, |
| gpointer user_data) |
| { |
| const char *cha = a; |
| const char *chb = b; |
| |
| /* just check that we got the right data */ |
| g_assert(GPOINTER_TO_INT(user_data) == 123); |
| |
| return *cha - *chb; |
| } |
| |
| static gint my_search(gconstpointer a, gconstpointer b) |
| { |
| return my_compare(b, a); |
| } |
| |
| static gpointer destroyed_key; |
| static gpointer destroyed_value; |
| static guint destroyed_key_count; |
| static guint destroyed_value_count; |
| |
| static void my_key_destroy(gpointer key) |
| { |
| destroyed_key = key; |
| destroyed_key_count++; |
| } |
| |
| static void my_value_destroy(gpointer value) |
| { |
| destroyed_value = value; |
| destroyed_value_count++; |
| } |
| |
| static gint my_traverse(gpointer key, gpointer value, gpointer data) |
| { |
| char *ch = key; |
| |
| g_assert((*ch) > 0); |
| |
| if (*ch == 'd') { |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| char chars[] = |
| "0123456789" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "abcdefghijklmnopqrstuvwxyz"; |
| |
| char chars2[] = |
| "0123456789" |
| "abcdefghijklmnopqrstuvwxyz"; |
| |
| static gint check_order(gpointer key, gpointer value, gpointer data) |
| { |
| char **p = data; |
| char *ch = key; |
| |
| g_assert(**p == *ch); |
| |
| (*p)++; |
| |
| return FALSE; |
| } |
| |
| static void test_tree_search(void) |
| { |
| gint i; |
| QTree *tree; |
| gboolean removed; |
| gchar c; |
| gchar *p, *d; |
| |
| tree = q_tree_new_with_data(my_compare_with_data, GINT_TO_POINTER(123)); |
| |
| for (i = 0; chars[i]; i++) { |
| q_tree_insert(tree, &chars[i], &chars[i]); |
| } |
| |
| q_tree_foreach(tree, my_traverse, NULL); |
| |
| g_assert(q_tree_nnodes(tree) == strlen(chars)); |
| g_assert(q_tree_height(tree) == 6); |
| |
| p = chars; |
| q_tree_foreach(tree, check_order, &p); |
| |
| for (i = 0; i < 26; i++) { |
| removed = q_tree_remove(tree, &chars[i + 10]); |
| g_assert(removed); |
| } |
| |
| c = '\0'; |
| removed = q_tree_remove(tree, &c); |
| g_assert(!removed); |
| |
| q_tree_foreach(tree, my_traverse, NULL); |
| |
| g_assert(q_tree_nnodes(tree) == strlen(chars2)); |
| g_assert(q_tree_height(tree) == 6); |
| |
| p = chars2; |
| q_tree_foreach(tree, check_order, &p); |
| |
| for (i = 25; i >= 0; i--) { |
| q_tree_insert(tree, &chars[i + 10], &chars[i + 10]); |
| } |
| |
| p = chars; |
| q_tree_foreach(tree, check_order, &p); |
| |
| c = '0'; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p && *p == c); |
| g_assert(q_tree_lookup_extended(tree, &c, (gpointer *)&d, (gpointer *)&p)); |
| g_assert(c == *d && c == *p); |
| |
| c = 'A'; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p && *p == c); |
| |
| c = 'a'; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p && *p == c); |
| |
| c = 'z'; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p && *p == c); |
| |
| c = '!'; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p == NULL); |
| |
| c = '='; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p == NULL); |
| |
| c = '|'; |
| p = q_tree_lookup(tree, &c); |
| g_assert(p == NULL); |
| |
| c = '0'; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p && *p == c); |
| |
| c = 'A'; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p && *p == c); |
| |
| c = 'a'; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p && *p == c); |
| |
| c = 'z'; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p && *p == c); |
| |
| c = '!'; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p == NULL); |
| |
| c = '='; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p == NULL); |
| |
| c = '|'; |
| p = q_tree_search(tree, my_search, &c); |
| g_assert(p == NULL); |
| |
| q_tree_destroy(tree); |
| } |
| |
| static void test_tree_remove(void) |
| { |
| QTree *tree; |
| char c, d; |
| gint i; |
| gboolean removed; |
| |
| tree = q_tree_new_full((GCompareDataFunc)my_compare, NULL, |
| my_key_destroy, |
| my_value_destroy); |
| |
| for (i = 0; chars[i]; i++) { |
| q_tree_insert(tree, &chars[i], &chars[i]); |
| } |
| |
| c = '0'; |
| q_tree_insert(tree, &c, &c); |
| g_assert(destroyed_key == &c); |
| g_assert(destroyed_value == &chars[0]); |
| destroyed_key = NULL; |
| destroyed_value = NULL; |
| |
| d = '1'; |
| q_tree_replace(tree, &d, &d); |
| g_assert(destroyed_key == &chars[1]); |
| g_assert(destroyed_value == &chars[1]); |
| destroyed_key = NULL; |
| destroyed_value = NULL; |
| |
| c = '2'; |
| removed = q_tree_remove(tree, &c); |
| g_assert(removed); |
| g_assert(destroyed_key == &chars[2]); |
| g_assert(destroyed_value == &chars[2]); |
| destroyed_key = NULL; |
| destroyed_value = NULL; |
| |
| c = '3'; |
| removed = q_tree_steal(tree, &c); |
| g_assert(removed); |
| g_assert(destroyed_key == NULL); |
| g_assert(destroyed_value == NULL); |
| |
| const gchar *remove = "omkjigfedba"; |
| for (i = 0; remove[i]; i++) { |
| removed = q_tree_remove(tree, &remove[i]); |
| g_assert(removed); |
| } |
| |
| q_tree_destroy(tree); |
| } |
| |
| static void test_tree_destroy(void) |
| { |
| QTree *tree; |
| gint i; |
| |
| tree = q_tree_new(my_compare); |
| |
| for (i = 0; chars[i]; i++) { |
| q_tree_insert(tree, &chars[i], &chars[i]); |
| } |
| |
| g_assert(q_tree_nnodes(tree) == strlen(chars)); |
| |
| g_test_message("nnodes: %d", q_tree_nnodes(tree)); |
| q_tree_ref(tree); |
| q_tree_destroy(tree); |
| |
| g_test_message("nnodes: %d", q_tree_nnodes(tree)); |
| g_assert(q_tree_nnodes(tree) == 0); |
| |
| q_tree_unref(tree); |
| } |
| |
| static void test_tree_insert(void) |
| { |
| QTree *tree; |
| gchar *p; |
| gint i; |
| gchar *scrambled; |
| |
| tree = q_tree_new(my_compare); |
| |
| for (i = 0; chars[i]; i++) { |
| q_tree_insert(tree, &chars[i], &chars[i]); |
| } |
| p = chars; |
| q_tree_foreach(tree, check_order, &p); |
| |
| q_tree_unref(tree); |
| tree = q_tree_new(my_compare); |
| |
| for (i = strlen(chars) - 1; i >= 0; i--) { |
| q_tree_insert(tree, &chars[i], &chars[i]); |
| } |
| p = chars; |
| q_tree_foreach(tree, check_order, &p); |
| |
| q_tree_unref(tree); |
| tree = q_tree_new(my_compare); |
| |
| scrambled = g_strdup(chars); |
| |
| for (i = 0; i < 30; i++) { |
| gchar tmp; |
| gint a, b; |
| |
| a = g_random_int_range(0, strlen(scrambled)); |
| b = g_random_int_range(0, strlen(scrambled)); |
| tmp = scrambled[a]; |
| scrambled[a] = scrambled[b]; |
| scrambled[b] = tmp; |
| } |
| |
| for (i = 0; scrambled[i]; i++) { |
| q_tree_insert(tree, &scrambled[i], &scrambled[i]); |
| } |
| p = chars; |
| q_tree_foreach(tree, check_order, &p); |
| |
| g_free(scrambled); |
| q_tree_unref(tree); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| g_test_init(&argc, &argv, NULL); |
| |
| g_test_add_func("/qtree/search", test_tree_search); |
| g_test_add_func("/qtree/remove", test_tree_remove); |
| g_test_add_func("/qtree/destroy", test_tree_destroy); |
| g_test_add_func("/qtree/insert", test_tree_insert); |
| |
| return g_test_run(); |
| } |