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();
|
||
|
}
|