test: add tests for hashmap

This commit is contained in:
CismonX 2025-03-08 13:52:57 +08:00
parent aed846b286
commit ca43bc6939
No known key found for this signature in database
GPG key ID: 3094873E29A482FB
6 changed files with 261 additions and 2 deletions

View file

@ -8,7 +8,8 @@
EXTRA_DIST = package.m4 testsuite.at $(TESTSUITE) $(TESTS_)
TESTS_ = lib_hash.at lib_prng.at lib_watcher.at lib_sandbox.at
TESTS_ = lib_hash.at lib_prng.at lib_watcher.at lib_sandbox.at \
lib_hashmap.at
# Helper programs for testing
@ -20,7 +21,8 @@ if BOOKMARKFS_UTIL
check_util_lib_CPPFLAGS = -I$(top_srcdir)/src
check_util_lib_LDADD = $(top_builddir)/src/libbookmarkfs_util.la
check_util_lib_SOURCES = check_lib.c check_watcher.c check_sandbox.c
check_util_lib_SOURCES = check_lib.c check_watcher.c check_sandbox.c \
check_hashmap.c
endif # BOOKMARKFS_UTIL
# Autotest setup

213
tests/check_hashmap.c Normal file
View file

@ -0,0 +1,213 @@
/**
* bookmarkfs/tests/check_hashmap.c
* ----
*
* Copyright (C) 2025 CismonX <admin@cismon.net>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <limits.h>
#include <sys/mman.h>
#include <unistd.h>
#include "check_lib.h"
#include "frontend_util.h"
#include "hashmap.h"
#include "prng.h"
struct check_item {
unsigned long id;
unsigned long val;
};
// Forward declaration start
static int check_one_round (struct hashmap *, struct check_item *, size_t);
static int do_check_hashmap (int, int);
static int item_comp_func (union hashmap_key, void const *);
static unsigned long
item_hash_func (void const *);
static void item_walk_func (void *, void *);
// Forward declaration end
static int
check_one_round (
struct hashmap *map,
struct check_item *items,
size_t items_cnt
) {
for (size_t i = 0; i < items_cnt; ++i) {
struct check_item *item = items + (prng_rand() % items_cnt);
unsigned long id = item->id;
unsigned long val = item->val;
union hashmap_key key = { .u64 = id };
unsigned long entry_id;
void *found = hashmap_search(map, key, val, &entry_id);
if (id & 1) {
if (found != item) {
log_puts("unexpected item in hashmap");
return -1;
}
hashmap_delete(map, item, entry_id);
} else {
if (found != NULL) {
log_puts("unexpected item in hashmap");
return -1;
}
hashmap_insert(map, val, item);
}
item->id = id ^ 1;
}
return 0;
}
int
do_check_hashmap (
int n,
int rounds
) {
size_t items_cnt = 1u << n;
size_t buf_size = sizeof(struct check_item) * items_cnt;
struct check_item *items = mmap(NULL, buf_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (items == MAP_FAILED) {
return -1;
}
struct hashmap *map = hashmap_create(item_comp_func, item_hash_func);
for (size_t i = 0; i < items_cnt; ++i) {
items[i] = (struct check_item) {
.id = i << 1,
.val = prng_rand(),
};
}
int status = -1;
for (int i = 0; i < rounds; ++i) {
if (0 != check_one_round(map, items, items_cnt)) {
goto end;
}
}
size_t cnt = 0;
hashmap_foreach(map, item_walk_func, &cnt);
for (size_t i = 0; i < items_cnt; ++i) {
struct check_item *item = items + i;
unsigned long id = item->id;
if (id & 1) {
--cnt;
hashmap_delete(map, item, -1);
}
}
if (cnt != 0) {
log_printf("bad item cnt %zu, expected 0", cnt);
goto end;
}
hashmap_foreach(map, item_walk_func, &cnt);
if (cnt != 0) {
log_printf("bad item cnt %zu, expected 0", cnt);
goto end;
}
status = 0;
end:
munmap(items, buf_size);
hashmap_destroy(map);
return status;
}
static int
item_comp_func (
union hashmap_key key,
void const *entry
) {
struct check_item const *item = entry;
return (key.u64 >> 1) - (item->id >> 1);
}
static unsigned long
item_hash_func (
void const *entry
) {
struct check_item const *item = entry;
return item->val;
}
static void
item_walk_func (
void *user_data,
void *UNUSED_VAR(entry)
) {
size_t *cnt_ptr = user_data;
++(*cnt_ptr);
}
int
check_hashmap (
int argc,
char *argv[]
) {
static uint64_t buf[4];
uint64_t *seed = NULL;
int n = -1;
int r = -1;
getopt_foreach(argc, argv, ":s:n:r:") {
case 's':
if (0 != prng_seed_from_hex(buf, optarg)) {
return -1;
}
seed = buf;
break;
case 'n':
n = atoi(optarg);
break;
case 'r':
r = atoi(optarg);
break;
default:
log_printf("bad option '-%c'", optopt);
return -1;
}
if (n < 10 || n > 30) {
log_printf("bad size %d", n);
return -1;
}
if (r < 0) {
log_printf("bad rounds cnt %d", r);
return -1;
}
if (0 != prng_seed(seed)) {
return -1;
}
return do_check_hashmap(n, r);
}

View file

@ -61,6 +61,8 @@ dispatch_subcmds (
status = check_watcher(argc, argv);
} else if (0 == strcmp("sandbox", cmd)) {
status = check_sandbox(argc, argv);
} else if (0 == strcmp("hashmap", cmd)) {
status = check_hashmap(argc, argv);
} else {
log_printf("bad subcmd '%s'", cmd);
}

View file

@ -35,6 +35,12 @@
action_if_false \
} while (0)
int
check_hashmap (
int argc,
char *argv[]
);
int
check_sandbox (
int argc,

35
tests/lib_hashmap.at Normal file
View file

@ -0,0 +1,35 @@
dnl
dnl Copyright (C) 2025 CismonX <admin@cismon.net>
dnl
dnl Copying and distribution of this file, with or without modification,
dnl are permitted in any medium without royalty,
dnl provided the copyright notice and this notice are preserved.
dnl This file is offered as-is, without any warranty.
dnl
AT_SETUP([util lib: hashmap])
AT_KEYWORDS([lib hashmap])
ATX_CHECK_LIB([
seed="${CHECK_HASHMAP_PRNG_SEED}"
if test -z "$seed"; then
seed=$(ath_fn_prng_seed)
fi
size="${CHECK_HASHMAP_DATA_SIZE}"
if test -z "$size"; then
# Requires at least 128 MiB of memory on a 64-bit platform.
size=22
fi
rounds="${CHECK_HASHMAP_ROUNDS}"
if test -z "$rounds"; then
rounds=5
fi
ATX_RUN([
check-util-lib hashmap -s "$seed" -n "$size" -r "$rounds"
])
])
AT_CLEANUP

View file

@ -107,3 +107,4 @@ m4_include([lib_hash.at])
m4_include([lib_prng.at])
m4_include([lib_watcher.at])
m4_include([lib_sandbox.at])
m4_include([lib_hashmap.at])