| /*****************************************************************************\ |
| * Copyright (C) 2012 CEA/DAM/DIF |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| #include <check.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "src/common/xhash.h" |
| #include "src/common/xmalloc.h" |
| |
| /* FIXME: how to check memory leaks with valgrind ? (to check if xhash_free |
| * does free all structures correctly). */ |
| |
| /***************************************************************************** |
| * DEFINITIONS |
| *****************************************************************************/ |
| |
| typedef struct hashable_st { |
| char id[255]; |
| uint32_t idn; |
| } hashable_t; |
| |
| void hashable_identify(void* voiditem, const char** key, uint32_t* key_len) |
| { |
| hashable_t* item = (hashable_t*)voiditem; |
| *key = item->id; |
| *key_len = strlen(item->id); |
| } |
| |
| /***************************************************************************** |
| * FIXTURE * |
| *****************************************************************************/ |
| |
| xhash_t* g_ht = NULL; |
| hashable_t g_hashables[200]; |
| uint32_t g_hashableslen = sizeof(g_hashables)/sizeof(g_hashables[0]); |
| |
| static void setup(void) |
| { |
| int i; |
| g_ht = xhash_init(hashable_identify, NULL); |
| if (!g_ht) return; /* fatal error, will be detected by test cases */ |
| for (i = 0; i < g_hashableslen; ++i) { |
| snprintf(g_hashables[i].id, sizeof(g_hashables[i].id), "%d", i); |
| g_hashables[i].idn = i; |
| /* it is an error if xhash_add returns null but it will be |
| * detected by test cases */ |
| if (!xhash_add(g_ht, g_hashables + i)) return; |
| } |
| } |
| |
| static void teardown(void) |
| { |
| xhash_free(g_ht); |
| } |
| |
| /***************************************************************************** |
| * UNIT TESTS * |
| ****************************************************************************/ |
| |
| START_TEST(test_init_free) |
| { |
| xhash_t* ht = NULL; |
| |
| mark_point(); |
| |
| /* invalid case */ |
| ht = xhash_init(NULL, NULL); |
| ck_assert_msg(ht == NULL, "allocated table without identifying function"); |
| |
| /* alloc and free */ |
| ht = xhash_init(hashable_identify, NULL); |
| ck_assert_msg(ht != NULL, "hash table was not allocated"); |
| xhash_free(ht); |
| } |
| END_TEST |
| |
| START_TEST(test_add) |
| { |
| xhash_t* ht = NULL; |
| hashable_t a[4] = {{"0", 0}, {"1", 1}, {"2", 2}, {"3", 3}}; |
| int i, len = sizeof(a)/sizeof(a[0]); |
| char buffer[255]; |
| ht = xhash_init(hashable_identify, NULL); |
| ck_assert_msg(xhash_add(NULL, a) == NULL, "invalid cases not null"); |
| ck_assert_msg(xhash_add(ht, NULL) == NULL, "invalid cases not null"); |
| ck_assert_msg(xhash_add(ht, a) != NULL, "xhash_add failed"); |
| ck_assert_msg(xhash_add(ht, a+1) != NULL, "xhash_add failed"); |
| ck_assert_msg(xhash_add(ht, a+2) != NULL, "xhash_add failed"); |
| ck_assert_msg(xhash_add(ht, a+3) != NULL, "xhash_add failed"); |
| for (i = 0; i < len; ++i) { |
| snprintf(buffer, sizeof(buffer), "%d", i); |
| ck_assert_msg(xhash_get_str(ht, buffer) == (a + i), |
| "bad hashable item returned"); |
| } |
| xhash_free(ht); |
| } |
| END_TEST |
| |
| START_TEST(test_find) |
| { |
| xhash_t* ht = g_ht; |
| char buffer[255]; |
| int i; |
| |
| /* test bad match */ |
| ck_assert_msg(xhash_get_str(ht, "bad") == NULL , "invalid case not null"); |
| ck_assert_msg(xhash_get_str(ht, "-1") == NULL , "invalid case not null"); |
| ck_assert_msg(xhash_get_str(ht, "10000") == NULL, "invalid case not null"); |
| |
| /* test all good indexes */ |
| for (i = 0; i < g_hashableslen; ++i) { |
| snprintf(buffer, sizeof(buffer), "%d", i); |
| ck_assert_msg(xhash_get_str(ht, buffer) == (g_hashables + i), |
| "bad hashable item returned"); |
| } |
| } |
| END_TEST |
| |
| /* returns the number of item deleted from the hash table */ |
| static int test_delete_helper() |
| { |
| xhash_t* ht = g_ht; |
| int ret = 0; |
| int i; |
| char buffer[255]; |
| for (i = 0; i < g_hashableslen; ++i) { |
| snprintf(buffer, sizeof(buffer), "%d", i); |
| if (xhash_get_str(ht, buffer) != (g_hashables + i)) { |
| ++ret; |
| } |
| } |
| return ret; |
| } |
| |
| START_TEST(test_delete) |
| { |
| xhash_t* ht = g_ht; |
| int result; |
| char buffer[255]; |
| |
| /* invalid cases */ |
| xhash_delete_str(NULL, "1"); |
| ck_assert_msg(xhash_get_str(ht, "1") != NULL, "invalid case null"); |
| /* Deleting non-existent item should do nothing. */ |
| xhash_delete(ht, NULL, 0); |
| ck_assert_msg(xhash_count(ht) == g_hashableslen, |
| "invalid delete has been done"); |
| result = test_delete_helper(); |
| ck_assert_msg(result == 0, |
| "no item should have been deleted, but %d were deleted", |
| result); |
| |
| /* test correct deletion */ |
| xhash_delete_str(ht, "10"); |
| ck_assert_msg(xhash_get_str(ht, "10") == NULL, "item not deleted"); |
| ck_assert_msg(xhash_count(ht) == (g_hashableslen-1), "bad count"); |
| /* left edge */ |
| xhash_delete_str(ht, "0"); |
| ck_assert_msg(xhash_get_str(ht, "0") == NULL, "item not deleted"); |
| ck_assert_msg(xhash_count(ht) == (g_hashableslen-2), "bad count"); |
| /* right edge */ |
| snprintf(buffer, sizeof(buffer), "%u", (g_hashableslen-2)); |
| xhash_delete_str(ht, buffer); |
| ck_assert_msg(xhash_get_str(ht, "0") == NULL, "item not deleted"); |
| ck_assert_msg(xhash_count(ht) == (g_hashableslen-3), "bad count"); |
| |
| result = test_delete_helper(); |
| ck_assert_msg(result == 3, "bad number of items were deleted: %d", |
| result); |
| } |
| END_TEST |
| |
| START_TEST(test_count) |
| { |
| xhash_t* ht = g_ht; |
| hashable_t a[4] = {{"0", 0}, {"1", 1}, {"2", 2}, {"3", 3}}; |
| ck_assert_msg(xhash_count(ht) == g_hashableslen, |
| "invalid count (fixture table)"); |
| ht = xhash_init(hashable_identify, NULL); |
| xhash_add(ht, a); |
| xhash_add(ht, a+1); |
| xhash_add(ht, a+2); |
| xhash_add(ht, a+3); |
| ck_assert_msg(xhash_count(ht) == 4, "invalid count (fresh table)"); |
| xhash_free(ht); |
| } |
| END_TEST |
| |
| static void test_walk_helper_callback(void* item, void* arg) |
| { |
| hashable_t* hashable = (hashable_t*)item; |
| hashable->idn = UINT32_MAX; |
| } |
| |
| START_TEST(test_walk) |
| { |
| xhash_t* ht = g_ht; |
| int i; |
| xhash_walk(ht, test_walk_helper_callback, NULL); |
| for (i = 0; i < g_hashableslen; ++i) { |
| ck_assert_msg(g_hashables[i].idn == UINT32_MAX, |
| "hashable item was not walked over"); |
| } |
| } |
| END_TEST |
| |
| /***************************************************************************** |
| * TEST SUITE * |
| ****************************************************************************/ |
| |
| Suite *xhash_suite(void) |
| { |
| Suite *s = suite_create("xhash"); |
| TCase *tc_core = tcase_create("Core"); |
| tcase_add_checked_fixture(tc_core, setup, teardown); |
| tcase_add_test(tc_core, test_init_free); |
| tcase_add_test(tc_core, test_add); |
| tcase_add_test(tc_core, test_find); |
| tcase_add_test(tc_core, test_delete); |
| tcase_add_test(tc_core, test_count); |
| tcase_add_test(tc_core, test_walk); |
| suite_add_tcase(s, tc_core); |
| return s; |
| } |
| |
| /***************************************************************************** |
| * TEST RUNNER * |
| ****************************************************************************/ |
| |
| int main(void) |
| { |
| int number_failed; |
| SRunner *sr = srunner_create(xhash_suite()); |
| |
| srunner_run_all(sr, CK_NORMAL); |
| number_failed = srunner_ntests_failed(sr); |
| srunner_free(sr); |
| |
| return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; |
| } |