#include #include #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; }