380 lines
9.7 KiB
C
380 lines
9.7 KiB
C
#include <check.h>
|
|
#include <string.h>
|
|
#include "ucore/htable.h"
|
|
#include "ucore/utils.h"
|
|
|
|
struct MyString {
|
|
char str[32];
|
|
struct UHNode node;
|
|
};
|
|
|
|
size_t hash_str(const char *str)
|
|
{
|
|
size_t hash = 31;
|
|
while (*str) {
|
|
hash *= *str++;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
size_t bad_hash(const char *str)
|
|
{
|
|
return str[0];
|
|
}
|
|
|
|
struct MyString *my_find(struct UHTable *table, const char *str, size_t hash)
|
|
{
|
|
struct UHNode *it;
|
|
|
|
UC_HTABLE_FOREACH_HASH(table, it, hash) {
|
|
struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node);
|
|
if (strcmp(my_str->str, str) == 0) {
|
|
return my_str;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
START_TEST (test_htable1)
|
|
struct MyString str[5] = {
|
|
{"One", {0,NULL}},
|
|
{"Two", {0,NULL}},
|
|
{"Three",{0,NULL}},
|
|
{"Four", {0,NULL}},
|
|
{"Five", {0,NULL}}
|
|
};
|
|
struct UHTable table;
|
|
int i;
|
|
|
|
uc_htable_init(&table, 3);
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
uc_htable_insert(&table, &str[i].node, hash_str(str[i].str));
|
|
}
|
|
|
|
ck_assert_int_eq(uc_htable_count(&table), 5);
|
|
|
|
fail_if(my_find(&table, "One", hash_str("One")) != &str[0]);
|
|
fail_if(my_find(&table, "Two", hash_str("Two")) != &str[1]);
|
|
fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]);
|
|
fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]);
|
|
fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]);
|
|
fail_if(my_find(&table, "NotHere", hash_str("NotHere")) != NULL);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_hash_collision)
|
|
//same as test1 but with a bad hash to force equal hash
|
|
struct MyString str[5] = {
|
|
{"...", {0,NULL}},
|
|
{"", {0,NULL}},
|
|
{".", {0,NULL}},
|
|
{"..", {0,NULL}},
|
|
{"....", {0,NULL}}
|
|
};
|
|
struct UHTable table;
|
|
int i;
|
|
|
|
uc_htable_init(&table, 1);
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
uc_htable_insert(&table, &str[i].node, bad_hash(str[i].str));
|
|
}
|
|
|
|
ck_assert_int_eq(uc_htable_count(&table), 5);
|
|
|
|
fail_if(my_find(&table, "....", bad_hash("....")) != &str[4]);
|
|
fail_if(my_find(&table, ".", bad_hash(".")) != &str[2]);
|
|
fail_if(my_find(&table, "", bad_hash("")) != &str[1]);
|
|
fail_if(my_find(&table, "..", bad_hash("..")) != &str[3]);
|
|
fail_if(my_find(&table, "...", bad_hash("...")) != &str[0]);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_resize)
|
|
struct MyString str[5] = {
|
|
{"One", {0,NULL}},
|
|
{"Two", {0,NULL}},
|
|
{"Three",{0,NULL}},
|
|
{"Four", {0,NULL}},
|
|
{"Five", {0,NULL}}
|
|
};
|
|
struct UHTable table;
|
|
int i;
|
|
int rc;
|
|
|
|
rc = uc_htable_init(&table, 3);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
uc_htable_insert(&table, &str[i].node, hash_str(str[i].str));
|
|
}
|
|
ck_assert_int_eq(uc_htable_count(&table), 5);
|
|
|
|
rc = uc_htable_resize(&table, 1024);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
ck_assert_int_eq(uc_htable_count(&table), 5);
|
|
|
|
fail_if(my_find(&table, "One", hash_str("One")) != &str[0]);
|
|
fail_if(my_find(&table, "Two", hash_str("Two")) != &str[1]);
|
|
fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]);
|
|
fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]);
|
|
fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_fail_init_resize)
|
|
int rc;
|
|
struct UHTable table;
|
|
rc = uc_htable_init(&table, 0);
|
|
ck_assert_int_ne(rc, 0);
|
|
|
|
rc = uc_htable_init(&table, 1);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
rc = uc_htable_resize(&table, 0);
|
|
ck_assert_int_ne(rc, 0);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_has_node)
|
|
struct MyString str1 = {"One", {0,NULL}};
|
|
struct MyString str2 = {"Two", {0,NULL}};
|
|
|
|
struct UHTable table;
|
|
int rc;
|
|
|
|
rc = uc_htable_init(&table, 3);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_insert(&table, &str1.node, hash_str(str1.str));
|
|
ck_assert_int_eq(uc_htable_count(&table), 1);
|
|
|
|
rc = uc_htable_has_node(&table, &str1.node);
|
|
ck_assert_int_ne(rc, 0);
|
|
|
|
rc = uc_htable_has_node(&table, &str2.node);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_remove)
|
|
struct MyString str1 = {"One", {0,NULL}};
|
|
struct MyString str2 = {"One", {0,NULL}};
|
|
struct MyString str3 = {"Other", {0,NULL}};
|
|
|
|
struct UHTable table;
|
|
int rc;
|
|
|
|
rc = uc_htable_init(&table, 1);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_insert(&table, &str1.node, hash_str(str1.str));
|
|
uc_htable_insert(&table, &str2.node, hash_str(str2.str));
|
|
uc_htable_insert(&table, &str3.node, hash_str(str3.str));
|
|
ck_assert_int_eq(uc_htable_count(&table), 3);
|
|
|
|
uc_htable_remove(&table, &str2.node);
|
|
ck_assert_int_eq(uc_htable_count(&table), 2);
|
|
|
|
//this one should still be here
|
|
fail_if(my_find(&table, "One", hash_str("One")) != &str1);
|
|
//
|
|
//thisone should be gone
|
|
rc = uc_htable_has_node(&table, &str2.node);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
|
|
uc_htable_remove(&table, &str1.node);
|
|
ck_assert_int_eq(uc_htable_count(&table), 1);
|
|
|
|
//now this one should be gone
|
|
rc = uc_htable_has_node(&table, &str1.node);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
//double removal should be ok
|
|
uc_htable_remove(&table, &str1.node);
|
|
ck_assert_int_eq(uc_htable_count(&table), 1);
|
|
|
|
//this one should still be here
|
|
fail_if(my_find(&table, "Other", hash_str("Other")) != &str3);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_remove_foreach)
|
|
struct MyString str1 = {"One", {0,NULL}};
|
|
struct MyString str2 = {"One", {0,NULL}};
|
|
struct MyString str3 = {"Other", {0,NULL}};
|
|
|
|
struct UHTable table;
|
|
struct UHNode *it;
|
|
struct UHNode *next;
|
|
int rc;
|
|
int removed = 0;
|
|
|
|
rc = uc_htable_init(&table, 1);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_insert(&table, &str1.node, hash_str(str1.str));
|
|
uc_htable_insert(&table, &str2.node, hash_str(str2.str));
|
|
uc_htable_insert(&table, &str3.node, hash_str(str3.str));
|
|
ck_assert_int_eq(uc_htable_count(&table), 3);
|
|
|
|
fail_if(my_find(&table, "One", hash_str("One")) == NULL);
|
|
|
|
UC_HTABLE_FOREACH_HASH_SAFE(&table, it, next, hash_str("One")) {
|
|
uc_htable_remove(&table, it);
|
|
memset(it, 0xff, sizeof *it);
|
|
removed++;
|
|
|
|
}
|
|
|
|
ck_assert_int_eq(removed, 2);
|
|
|
|
fail_if(my_find(&table, "One", hash_str("One")) != NULL);
|
|
fail_if(my_find(&table, "Other", hash_str("Other")) != &str3);
|
|
ck_assert_int_eq(uc_htable_count(&table), 1);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_clear)
|
|
struct MyString str1 = {"One", {0,NULL}};
|
|
|
|
struct UHTable table;
|
|
int rc;
|
|
|
|
rc = uc_htable_init(&table, 100);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_insert(&table, &str1.node, hash_str(str1.str));
|
|
ck_assert_int_eq(uc_htable_count(&table), 1);
|
|
|
|
fail_if(my_find(&table, "One", hash_str("One")) != &str1);
|
|
|
|
uc_htable_clear(&table);
|
|
ck_assert_int_eq(uc_htable_count(&table), 0);
|
|
fail_if(my_find(&table, "One", hash_str("One")) != NULL);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_foreach)
|
|
struct MyString str1 = {"One", {0,NULL}};
|
|
struct MyString str2 = {"One", {0,NULL}};
|
|
struct MyString str3 = {"Other",{0,NULL}};
|
|
|
|
struct UHTable table;
|
|
struct UHNode *it;
|
|
int rc;
|
|
int found1 = 0;
|
|
int found2 = 0;
|
|
int found3 = 0;
|
|
|
|
rc = uc_htable_init(&table, 1000);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_insert(&table, &str1.node, hash_str(str1.str));
|
|
uc_htable_insert(&table, &str2.node, hash_str(str2.str));
|
|
uc_htable_insert(&table, &str3.node, hash_str(str3.str));
|
|
ck_assert_int_eq(uc_htable_count(&table), 3);
|
|
|
|
UC_HTABLE_FOREACH(&table, it) {
|
|
struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node);
|
|
if (my_str == &str1) {
|
|
found1++;
|
|
}
|
|
if (my_str == &str2) {
|
|
found2++;
|
|
}
|
|
if (my_str == &str3) {
|
|
found3++;
|
|
}
|
|
}
|
|
|
|
ck_assert_int_eq(found1, 1);
|
|
ck_assert_int_eq(found2, 1);
|
|
ck_assert_int_eq(found3, 1);
|
|
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
START_TEST (test_htable_foreach_safe)
|
|
struct MyString str1 = {"One", {0,NULL}};
|
|
struct MyString str2 = {"One", {0,NULL}};
|
|
struct MyString str3 = {"Other",{0,NULL}};
|
|
|
|
struct UHTable table;
|
|
struct UHNode *it;
|
|
struct UHNode *next;
|
|
int rc;
|
|
|
|
rc = uc_htable_init(&table, 1000);
|
|
ck_assert_int_eq(rc, 0);
|
|
|
|
uc_htable_insert(&table, &str1.node, hash_str(str1.str));
|
|
uc_htable_insert(&table, &str2.node, hash_str(str2.str));
|
|
uc_htable_insert(&table, &str3.node, hash_str(str3.str));
|
|
|
|
fail_if(my_find(&table, "Other", hash_str("Other")) != &str3);
|
|
|
|
ck_assert_int_eq(uc_htable_count(&table), 3);
|
|
|
|
UC_HTABLE_FOREACH_SAFE(&table, it, next) {
|
|
uc_htable_remove(&table, it);
|
|
memset(it, 0xff, sizeof *it);
|
|
}
|
|
|
|
fail_if(my_find(&table, "One", hash_str("One")) != NULL);
|
|
fail_if(my_find(&table, "Other", hash_str("Other")) != NULL);
|
|
|
|
ck_assert_int_eq(uc_htable_count(&table), 0);
|
|
|
|
uc_htable_destroy(&table);
|
|
|
|
END_TEST
|
|
|
|
Suite *htable_suite(void)
|
|
{
|
|
Suite *s = suite_create("htable");
|
|
TCase *tc = tcase_create("htable tests");
|
|
|
|
tcase_add_test(tc, test_htable1);
|
|
tcase_add_test(tc, test_htable_hash_collision);
|
|
tcase_add_test(tc, test_htable_resize);
|
|
tcase_add_test(tc, test_htable_fail_init_resize);
|
|
tcase_add_test(tc, test_htable_has_node);
|
|
tcase_add_test(tc, test_htable_remove);
|
|
tcase_add_test(tc, test_htable_remove_foreach);
|
|
tcase_add_test(tc, test_htable_clear);
|
|
tcase_add_test(tc, test_htable_foreach);
|
|
tcase_add_test(tc, test_htable_foreach_safe);
|
|
|
|
|
|
suite_add_tcase(s, tc);
|
|
|
|
return s;
|
|
}
|
|
|