334 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			334 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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(); | ||
|  | } |