/*****************************************************************************\ * Copyright (C) 2012 CEA/DAM/DIF * * This file is part of Slurm, a resource management program. * For details, see . * 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 #include #include #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); fail_unless(ht == NULL, "allocated table without identifying function"); /* alloc and free */ ht = xhash_init(hashable_identify, NULL); fail_unless(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); fail_unless(xhash_add(NULL, a) == NULL, "invalid cases not null"); fail_unless(xhash_add(ht, NULL) == NULL, "invalid cases not null"); fail_unless(xhash_add(ht, a) != NULL, "xhash_add failed"); fail_unless(xhash_add(ht, a+1) != NULL, "xhash_add failed"); fail_unless(xhash_add(ht, a+2) != NULL, "xhash_add failed"); fail_unless(xhash_add(ht, a+3) != NULL, "xhash_add failed"); for (i = 0; i < len; ++i) { snprintf(buffer, sizeof(buffer), "%d", i); fail_unless(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 */ fail_unless(xhash_get_str(ht, "bad") == NULL , "invalid case not null"); fail_unless(xhash_get_str(ht, "-1") == NULL , "invalid case not null"); fail_unless(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); fail_unless(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"); fail_unless(xhash_get_str(ht, "1") != NULL, "invalid case null"); /* Deleting non-existent item should do nothing. */ xhash_delete(ht, NULL, 0); fail_unless(xhash_count(ht) == g_hashableslen, "invalid delete has been done"); result = test_delete_helper(); fail_unless(result == 0, "no item should have been deleted, but %d were deleted", result); /* test correct deletion */ xhash_delete_str(ht, "10"); fail_unless(xhash_get_str(ht, "10") == NULL, "item not deleted"); fail_unless(xhash_count(ht) == (g_hashableslen-1), "bad count"); /* left edge */ xhash_delete_str(ht, "0"); fail_unless(xhash_get_str(ht, "0") == NULL, "item not deleted"); fail_unless(xhash_count(ht) == (g_hashableslen-2), "bad count"); /* right edge */ snprintf(buffer, sizeof(buffer), "%u", (g_hashableslen-2)); xhash_delete_str(ht, buffer); fail_unless(xhash_get_str(ht, "0") == NULL, "item not deleted"); fail_unless(xhash_count(ht) == (g_hashableslen-3), "bad count"); result = test_delete_helper(); fail_unless(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}}; fail_unless(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); fail_unless(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) { fail_unless(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; }