hashmap: Add test cases
Also get rid of the silly hashmap_foo demo program.
This commit is contained in:
parent
d539b920c9
commit
0ab36ec37e
3 changed files with 626 additions and 142 deletions
|
|
@ -51,3 +51,8 @@ TESTS += resizable.test
|
|||
check_PROGRAMS += resizable.test
|
||||
resizable_test_SOURCES = resizable_test.c test.h resizable.h
|
||||
resizable_test_LDADD = libapfl.a
|
||||
|
||||
TESTS += hashmap.test
|
||||
check_PROGRAMS += hashmap.test
|
||||
hashmap_test_SOURCES = hashmap_test.c test.h hashmap.h
|
||||
hashmap_test_LDADD = libapfl.a
|
||||
|
|
|
|||
|
|
@ -1,142 +0,0 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
bool keys_eq_impl(void *opaque, const void *_a, const void *_b)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
const char * const *a = _a;
|
||||
const char * const *b = _b;
|
||||
return strcmp(*a, *b) == 0;
|
||||
}
|
||||
|
||||
apfl_hash calc_hash_impl(void *opaque, const void *_key)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
const char * const *key = _key;
|
||||
|
||||
return apfl_hash_fnv1a(*key, strlen(*key));
|
||||
}
|
||||
|
||||
void destroy_kv_impl(void *opaque, void *_data)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
char **data = _data;
|
||||
free(*data);
|
||||
}
|
||||
|
||||
bool copy_kv_impl(void *opaque, void *_dest, const void *_src)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
char **dest = _dest;
|
||||
const char * const *src = _src;
|
||||
|
||||
*dest = malloc(strlen(*src) + 1);
|
||||
if (*dest == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strcpy(*dest, *src);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define BUFSIZE 10000
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
struct apfl_hashmap_callbacks callbacks = {
|
||||
.opaque = NULL,
|
||||
.keys_eq = keys_eq_impl,
|
||||
.calc_hash = calc_hash_impl,
|
||||
.destroy_key = destroy_kv_impl,
|
||||
.destroy_value = destroy_kv_impl,
|
||||
.copy_key = copy_kv_impl,
|
||||
.copy_value = copy_kv_impl,
|
||||
};
|
||||
|
||||
apfl_hashmap map = apfl_hashmap_new(callbacks, sizeof(char *), sizeof(char *));
|
||||
if (map == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char line[BUFSIZE];
|
||||
|
||||
char key[BUFSIZE];
|
||||
char *keyp = &key[0];
|
||||
char value[BUFSIZE];
|
||||
char *valuep = &value[0];
|
||||
|
||||
char *retreived = NULL;
|
||||
|
||||
for (;;) {
|
||||
if (fgets(line, BUFSIZE, stdin) == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
char *tok = strtok(line, " ");
|
||||
if (tok == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(tok, "set") == 0) {
|
||||
tok = strtok(NULL, " \n");
|
||||
strcpy(key, tok);
|
||||
|
||||
tok = strtok(NULL, " \n");
|
||||
strcpy(value, tok);
|
||||
|
||||
if (!apfl_hashmap_set(map, &keyp, &valuep)) {
|
||||
return 2;
|
||||
}
|
||||
} else if (strcmp(tok, "get") == 0 ) {
|
||||
tok = strtok(NULL, " \n");
|
||||
strcpy(key, tok);
|
||||
|
||||
if (apfl_hashmap_get(map, &keyp, &retreived)) {
|
||||
printf("%s => %s\n", key, retreived);
|
||||
free(retreived);
|
||||
continue;
|
||||
}
|
||||
} else if (strcmp(tok, "del") == 0 ) {
|
||||
tok = strtok(NULL, " \n");
|
||||
strcpy(key, tok);
|
||||
|
||||
apfl_hashmap_delete(map, &keyp);
|
||||
} else if (strcmp(tok, "list") == 0) {
|
||||
apfl_hashmap_cursor cur = apfl_hashmap_get_cursor(map);
|
||||
if (cur == NULL) {
|
||||
return 3;
|
||||
}
|
||||
for (; !apfl_hashmap_cursor_is_end(cur); apfl_hashmap_cursor_next(cur)) {
|
||||
char *k = NULL;
|
||||
char *v = NULL;
|
||||
|
||||
if (
|
||||
apfl_hashmap_cursor_get_key(cur, &k)
|
||||
&& apfl_hashmap_cursor_get_value(cur, &v)
|
||||
) {
|
||||
printf("%s => %s\n", k, v);
|
||||
}
|
||||
free(k);
|
||||
free(v);
|
||||
}
|
||||
|
||||
apfl_hashmap_cursor_destroy(cur);
|
||||
}
|
||||
}
|
||||
|
||||
apfl_hashmap_destroy(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
621
src/hashmap_test.c
Normal file
621
src/hashmap_test.c
Normal file
|
|
@ -0,0 +1,621 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "test.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
static struct apfl_hashmap
|
||||
init_int2int(testctx t)
|
||||
{
|
||||
struct apfl_hashmap map;
|
||||
if (!apfl_hashmap_init(
|
||||
&map,
|
||||
(struct apfl_hashmap_callbacks) { .opaque = NULL },
|
||||
sizeof(int),
|
||||
sizeof(int)
|
||||
)) {
|
||||
test_fatalf(t, "Failed initializing int2int map!");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static void
|
||||
assert_len(testctx t, struct apfl_hashmap map, size_t want, const char *label)
|
||||
{
|
||||
size_t have = apfl_hashmap_count(map);
|
||||
if (have != want) {
|
||||
test_failf(t, "%s: expected count to be %d, got %d instead", label, want, have);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_get_int2int(testctx t, struct apfl_hashmap *map, int k, int v)
|
||||
{
|
||||
int have;
|
||||
if (apfl_hashmap_get(map, &k, &have)) {
|
||||
if (have != v) {
|
||||
test_failf(t, "Got %d instead of %d for key %d", have, v, k);
|
||||
}
|
||||
} else {
|
||||
test_failf(t, "Failed getting %d (expected %d)", k, v);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_set_int2int(testctx t, struct apfl_hashmap *map, int k, int v)
|
||||
{
|
||||
if (!apfl_hashmap_set(map, &k, &v)) {
|
||||
test_fatalf(t, "Failed setting %d -> %d", k, v);
|
||||
}
|
||||
|
||||
check_get_int2int(t, map, k, v);
|
||||
}
|
||||
|
||||
static void
|
||||
check_not_exists_int2int(testctx t, struct apfl_hashmap *map, int k)
|
||||
{
|
||||
int v;
|
||||
if (apfl_hashmap_get(map, &k, &v)) {
|
||||
test_failf(t, "Expected key %d to not be set, but got value %d", k, v);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
check_get_str2str(testctx t, struct apfl_hashmap *map, const char *k, const char *v)
|
||||
{
|
||||
char *have;
|
||||
if (apfl_hashmap_get(map, &k, &have)) {
|
||||
if (strcmp(v, have) != 0) {
|
||||
test_failf(t, "Got %s instead of %s for key %s", have, v, k);
|
||||
}
|
||||
} else {
|
||||
test_failf(t, "Failed getting %s (expected %s)", k, v);
|
||||
}
|
||||
|
||||
free(have);
|
||||
}
|
||||
|
||||
static void
|
||||
check_set_str2str(testctx t, struct apfl_hashmap *map, const char *k, const char *v)
|
||||
{
|
||||
if (!apfl_hashmap_set(map, &k, &v)) {
|
||||
test_fatalf(t, "Failed setting %s -> %s", k, v);
|
||||
}
|
||||
|
||||
check_get_str2str(t, map, k, v);
|
||||
}
|
||||
|
||||
static void
|
||||
check_not_exists_str2str(testctx t, struct apfl_hashmap *map, const char *k)
|
||||
{
|
||||
char *v;
|
||||
if (apfl_hashmap_get(map, &k, &v)) {
|
||||
test_failf(t, "Expected key %s to not be set, but got value %s", k, v);
|
||||
free(v);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
str2str_keys_eq(void *_opaque, const void *_a, const void *_b)
|
||||
{
|
||||
testctx t = _opaque;
|
||||
const char * const *a = _a;
|
||||
const char * const *b = _b;
|
||||
|
||||
if (a == b) {
|
||||
test_fatalf(t, "keys_eq callback: a == b, this should not happen!");
|
||||
}
|
||||
if (*a == *b) {
|
||||
test_fatalf(t, "keys_eq callback: *a == *b, this should not happen!");
|
||||
}
|
||||
|
||||
return strcmp(*a, *b) == 0;
|
||||
}
|
||||
|
||||
static apfl_hash
|
||||
str2str_calc_hash(void *opaque, const void *_key)
|
||||
{
|
||||
(void)opaque;
|
||||
const char * const *key = _key;
|
||||
|
||||
return apfl_hash_fnv1a(*key, strlen(*key));
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_str_kv(char **strptr)
|
||||
{
|
||||
free(*strptr);
|
||||
*strptr = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
str2str_destroy_key(void *opaque, void *_key)
|
||||
{
|
||||
(void)opaque;
|
||||
destroy_str_kv(_key);
|
||||
}
|
||||
|
||||
static void
|
||||
str2str_destroy_value(void *opaque, void *_value)
|
||||
{
|
||||
(void)opaque;
|
||||
destroy_str_kv(_value);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_str_kv(testctx t, char **dest, char **src)
|
||||
{
|
||||
size_t len = strlen(*src);
|
||||
*dest = must_alloc(t, len + 1);
|
||||
memcpy(*dest, *src, len + 1);
|
||||
}
|
||||
|
||||
static void
|
||||
str2str_copy_key(void *_opaque, void *_dest, void *_src)
|
||||
{
|
||||
testctx t = _opaque;
|
||||
copy_str_kv(t, _dest, _src);
|
||||
}
|
||||
|
||||
static void
|
||||
str2str_copy_value(void *_opaque, void *_dest, void *_src)
|
||||
{
|
||||
testctx t = _opaque;
|
||||
copy_str_kv(t, _dest, _src);
|
||||
}
|
||||
|
||||
static struct apfl_hashmap
|
||||
init_str2str(testctx t)
|
||||
{
|
||||
struct apfl_hashmap map;
|
||||
if (!apfl_hashmap_init(
|
||||
&map,
|
||||
(struct apfl_hashmap_callbacks) {
|
||||
.opaque = t,
|
||||
.keys_eq = str2str_keys_eq,
|
||||
.calc_hash = str2str_calc_hash,
|
||||
.destroy_key = str2str_destroy_key,
|
||||
.destroy_value = str2str_destroy_value,
|
||||
.copy_key = str2str_copy_key,
|
||||
.copy_value = str2str_copy_value,
|
||||
},
|
||||
sizeof(char *),
|
||||
sizeof(char *)
|
||||
)) {
|
||||
test_fatalf(t, "Failed initializing str2str map!");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
TEST(int2int_set_get_delete, t) {
|
||||
struct apfl_hashmap map = init_int2int(t);
|
||||
assert_len(t, map, 0, "after init");
|
||||
|
||||
int k = 1337;
|
||||
check_set_int2int(t, &map, k, 666);
|
||||
assert_len(t, map, 1, "after setting");
|
||||
|
||||
apfl_hashmap_delete(&map, &k);
|
||||
|
||||
check_not_exists_int2int(t, &map, k);
|
||||
|
||||
assert_len(t, map, 0, "after deleting");
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
TEST(int2int_set_overwrite, t) {
|
||||
struct apfl_hashmap map = init_int2int(t);
|
||||
assert_len(t, map, 0, "after init");
|
||||
|
||||
check_set_int2int(t, &map, 42, 1337);
|
||||
assert_len(t, map, 1, "after setting");
|
||||
|
||||
check_set_int2int(t, &map, 42, 666);
|
||||
assert_len(t, map, 1, "after setting again");
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
TEST(int2int_set_many, t) {
|
||||
struct apfl_hashmap map = init_int2int(t);
|
||||
assert_len(t, map, 0, "after init");
|
||||
|
||||
check_set_int2int(t, &map, 42, 1337);
|
||||
assert_len(t, map, 1, "after setting 42");
|
||||
|
||||
check_set_int2int(t, &map, 123, 456);
|
||||
assert_len(t, map, 2, "after setting 123");
|
||||
|
||||
check_set_int2int(t, &map, 42, 666);
|
||||
assert_len(t, map, 2, "after setting 42 again");
|
||||
|
||||
check_set_int2int(t, &map, 1, 2);
|
||||
assert_len(t, map, 3, "after setting 1");
|
||||
|
||||
check_set_int2int(t, &map, 0, 0);
|
||||
assert_len(t, map, 4, "after setting 0");
|
||||
|
||||
// Let's check, if they are still in the map
|
||||
check_get_int2int(t, &map, 42, 666);
|
||||
check_get_int2int(t, &map, 123, 456);
|
||||
check_get_int2int(t, &map, 1, 2);
|
||||
check_get_int2int(t, &map, 0, 0);
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
#define ITERATING_SWITCH_CASE(t, key, seen, value_want, value_have) \
|
||||
case key: \
|
||||
if (seen) { \
|
||||
test_failf( \
|
||||
t, \
|
||||
"already seen key %d, got it again (with value=%d)", \
|
||||
key, \
|
||||
value_have \
|
||||
); \
|
||||
} else { \
|
||||
seen = true; \
|
||||
if (value_have != value_want) { \
|
||||
test_failf( \
|
||||
t, \
|
||||
"got key %d but with wrong value: want=%d, have=%d", \
|
||||
key, \
|
||||
value_want, \
|
||||
value_have \
|
||||
); \
|
||||
} \
|
||||
} \
|
||||
break;
|
||||
|
||||
TEST(int2int_iterating, t) {
|
||||
struct apfl_hashmap map = init_int2int(t);
|
||||
|
||||
check_set_int2int(t, &map, 123, 456);
|
||||
check_set_int2int(t, &map, 42, 666);
|
||||
check_set_int2int(t, &map, 1, 2);
|
||||
check_set_int2int(t, &map, 0, 0);
|
||||
|
||||
bool seen_123 = false;
|
||||
bool seen_42 = false;
|
||||
bool seen_1 = false;
|
||||
bool seen_0 = false;
|
||||
|
||||
for (
|
||||
struct apfl_hashmap_cursor it = apfl_hashmap_get_cursor(&map);
|
||||
!apfl_hashmap_cursor_is_end(it);
|
||||
apfl_hashmap_cursor_next(&it)
|
||||
) {
|
||||
int k, v;
|
||||
|
||||
apfl_hashmap_cursor_get_key(it, &k);
|
||||
apfl_hashmap_cursor_get_value(it, &v);
|
||||
|
||||
switch (k) {
|
||||
ITERATING_SWITCH_CASE(t, 123, seen_123, 456, v)
|
||||
ITERATING_SWITCH_CASE(t, 42, seen_42, 666, v)
|
||||
ITERATING_SWITCH_CASE(t, 1, seen_1, 2, v)
|
||||
ITERATING_SWITCH_CASE(t, 0, seen_0, 0, v)
|
||||
default:
|
||||
test_failf(t, "Seen unexpected key %d with value %d", k, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!seen_123) {
|
||||
test_failf(t, "Did not see key 123");
|
||||
}
|
||||
if (!seen_42) {
|
||||
test_failf(t, "Did not see key 42");
|
||||
}
|
||||
if (!seen_1) {
|
||||
test_failf(t, "Did not see key 1");
|
||||
}
|
||||
if (!seen_0) {
|
||||
test_failf(t, "Did not see key 0");
|
||||
}
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
TEST(int2int_moving, t) {
|
||||
struct apfl_hashmap orig = init_int2int(t);
|
||||
|
||||
check_set_int2int(t, &orig, 011, 899);
|
||||
check_set_int2int(t, &orig, 988, 199);
|
||||
check_set_int2int(t, &orig, 911, 972);
|
||||
check_set_int2int(t, &orig, 5, 3);
|
||||
|
||||
struct apfl_hashmap new = apfl_hashmap_move(&orig);
|
||||
|
||||
assert_len(t, new, 4, "after moving");
|
||||
check_get_int2int(t, &new, 011, 899);
|
||||
check_get_int2int(t, &new, 988, 199);
|
||||
check_get_int2int(t, &new, 911, 972);
|
||||
check_get_int2int(t, &new, 5, 3);
|
||||
|
||||
apfl_hashmap_deinit(&new);
|
||||
apfl_hashmap_deinit(&orig); // not neccessary, but should still work without crashing
|
||||
}
|
||||
|
||||
TEST(int2int_copying, t) {
|
||||
struct apfl_hashmap orig = init_int2int(t);
|
||||
|
||||
check_set_int2int(t, &orig, 1, 111);
|
||||
check_set_int2int(t, &orig, 2, 222);
|
||||
check_set_int2int(t, &orig, 3, 333);
|
||||
check_set_int2int(t, &orig, 4, 444);
|
||||
|
||||
struct apfl_hashmap copy;
|
||||
if (!apfl_hashmap_copy(©, orig)) {
|
||||
test_fatalf(t, "Copying failed");
|
||||
}
|
||||
|
||||
assert_len(t, orig, 4, "orig after moving");
|
||||
check_get_int2int(t, &orig, 1, 111);
|
||||
check_get_int2int(t, &orig, 2, 222);
|
||||
check_get_int2int(t, &orig, 3, 333);
|
||||
check_get_int2int(t, &orig, 4, 444);
|
||||
|
||||
assert_len(t, copy, 4, "copy after moving");
|
||||
check_get_int2int(t, ©, 1, 111);
|
||||
check_get_int2int(t, ©, 2, 222);
|
||||
check_get_int2int(t, ©, 3, 333);
|
||||
check_get_int2int(t, ©, 4, 444);
|
||||
|
||||
// Setting something in orig should not influence the copy
|
||||
check_set_int2int(t, &orig, 5, 555);
|
||||
check_not_exists_int2int(t, ©, 5);
|
||||
|
||||
// And the other way around too
|
||||
check_set_int2int(t, ©, 6, 666);
|
||||
check_not_exists_int2int(t, &orig, 6);
|
||||
|
||||
// Overwriting something in orig should not influence the copy
|
||||
check_set_int2int(t, &orig, 1, 1337);
|
||||
check_get_int2int(t, ©, 1, 111);
|
||||
|
||||
// And the other way around too
|
||||
check_set_int2int(t, ©, 2, 1337);
|
||||
check_get_int2int(t, &orig, 2, 222);
|
||||
|
||||
int k;
|
||||
// Deleting something in orig should not influence the copy
|
||||
k = 3;
|
||||
apfl_hashmap_delete(&orig, &k);
|
||||
check_get_int2int(t, ©, 3, 333);
|
||||
|
||||
// And the other way around too
|
||||
k = 4;
|
||||
apfl_hashmap_delete(©, &k);
|
||||
check_get_int2int(t, &orig, 4, 444);
|
||||
|
||||
apfl_hashmap_deinit(©);
|
||||
apfl_hashmap_deinit(&orig);
|
||||
}
|
||||
|
||||
TEST(str2str_set_get_delete, t) {
|
||||
struct apfl_hashmap map = init_str2str(t);
|
||||
assert_len(t, map, 0, "after init");
|
||||
|
||||
const char *k = "foo";
|
||||
check_set_str2str(t, &map, k, "bar");
|
||||
assert_len(t, map, 1, "after setting");
|
||||
|
||||
apfl_hashmap_delete(&map, &k);
|
||||
|
||||
check_not_exists_str2str(t, &map, k);
|
||||
|
||||
assert_len(t, map, 0, "after deleting");
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
TEST(str2str_set_overwrite, t) {
|
||||
struct apfl_hashmap map = init_str2str(t);
|
||||
assert_len(t, map, 0, "after init");
|
||||
|
||||
check_set_str2str(t, &map, "foo", "bar");
|
||||
assert_len(t, map, 1, "after setting");
|
||||
|
||||
check_set_str2str(t, &map, "foo", "baz");
|
||||
assert_len(t, map, 1, "after setting again");
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
TEST(str2str_set_many, t) {
|
||||
struct apfl_hashmap map = init_str2str(t);
|
||||
assert_len(t, map, 0, "after init");
|
||||
|
||||
check_set_str2str(t, &map, "foo", "abc");
|
||||
assert_len(t, map, 1, "after setting foo");
|
||||
|
||||
check_set_str2str(t, &map, "bar", "def");
|
||||
assert_len(t, map, 2, "after setting bar");
|
||||
|
||||
check_set_str2str(t, &map, "foo", "ghi");
|
||||
assert_len(t, map, 2, "after setting foo again");
|
||||
|
||||
check_set_str2str(t, &map, "baz", "jkl");
|
||||
assert_len(t, map, 3, "after setting 1");
|
||||
|
||||
check_set_str2str(t, &map, "", "");
|
||||
assert_len(t, map, 4, "after setting \"\"");
|
||||
|
||||
// Let's check, if they are still in the map
|
||||
check_get_str2str(t, &map, "foo", "ghi");
|
||||
check_get_str2str(t, &map, "bar", "def");
|
||||
check_get_str2str(t, &map, "baz", "jkl");
|
||||
check_get_str2str(t, &map, "", "");
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
#define ITERATING_SEEN_CHECK(t, key, seen, value_want, value_have) \
|
||||
if (seen) { \
|
||||
test_failf( \
|
||||
t, \
|
||||
"already seen key %s, got it again (with value=%s)", \
|
||||
key, \
|
||||
value_have \
|
||||
); \
|
||||
} else { \
|
||||
seen = true; \
|
||||
if (strcmp(value_have, value_want) != 0) { \
|
||||
test_failf( \
|
||||
t, \
|
||||
"got key %s but with wrong value: want=%s, have=%s", \
|
||||
key, \
|
||||
value_want, \
|
||||
value_have \
|
||||
); \
|
||||
} \
|
||||
} \
|
||||
|
||||
TEST(str2str_iterating, t) {
|
||||
struct apfl_hashmap map = init_str2str(t);
|
||||
|
||||
check_set_str2str(t, &map, "foo", "abc");
|
||||
check_set_str2str(t, &map, "bar", "def");
|
||||
check_set_str2str(t, &map, "baz", "ghi");
|
||||
check_set_str2str(t, &map, "", "");
|
||||
|
||||
bool seen_foo = false;
|
||||
bool seen_bar = false;
|
||||
bool seen_baz = false;
|
||||
bool seen_blank = false;
|
||||
|
||||
for (
|
||||
struct apfl_hashmap_cursor it = apfl_hashmap_get_cursor(&map);
|
||||
!apfl_hashmap_cursor_is_end(it);
|
||||
apfl_hashmap_cursor_next(&it)
|
||||
) {
|
||||
char *k;
|
||||
char *v;
|
||||
|
||||
apfl_hashmap_cursor_get_key(it, &k);
|
||||
apfl_hashmap_cursor_get_value(it, &v);
|
||||
|
||||
if (strcmp(k, "foo") == 0) {
|
||||
ITERATING_SEEN_CHECK(t, "foo", seen_foo, "abc", v)
|
||||
} else if (strcmp(k, "bar") == 0) {
|
||||
ITERATING_SEEN_CHECK(t, "bar", seen_bar, "def", v)
|
||||
} else if (strcmp(k, "baz") == 0) {
|
||||
ITERATING_SEEN_CHECK(t, "baz", seen_baz, "ghi", v)
|
||||
} else if (strcmp(k, "") == 0) {
|
||||
ITERATING_SEEN_CHECK(t, "", seen_blank, "", v)
|
||||
} else {
|
||||
test_failf(t, "Seen unexpected key %s with value %s", k, v);
|
||||
}
|
||||
|
||||
free(k);
|
||||
free(v);
|
||||
}
|
||||
|
||||
if (!seen_foo) {
|
||||
test_failf(t, "Did not see key foo");
|
||||
}
|
||||
if (!seen_bar) {
|
||||
test_failf(t, "Did not see key bar");
|
||||
}
|
||||
if (!seen_baz) {
|
||||
test_failf(t, "Did not see key baz");
|
||||
}
|
||||
if (!seen_blank) {
|
||||
test_failf(t, "Did not see key \"\"");
|
||||
}
|
||||
|
||||
apfl_hashmap_deinit(&map);
|
||||
}
|
||||
|
||||
TEST(str2str_moving, t) {
|
||||
struct apfl_hashmap orig = init_str2str(t);
|
||||
|
||||
check_set_str2str(t, &orig, "foo", "abc");
|
||||
check_set_str2str(t, &orig, "bar", "def");
|
||||
check_set_str2str(t, &orig, "baz", "ghi");
|
||||
check_set_str2str(t, &orig, "quux", "jkl");
|
||||
|
||||
struct apfl_hashmap new = apfl_hashmap_move(&orig);
|
||||
|
||||
assert_len(t, new, 4, "after moving");
|
||||
check_get_str2str(t, &new, "foo", "abc");
|
||||
check_get_str2str(t, &new, "bar", "def");
|
||||
check_get_str2str(t, &new, "baz", "ghi");
|
||||
check_get_str2str(t, &new, "quux", "jkl");
|
||||
|
||||
apfl_hashmap_deinit(&new);
|
||||
apfl_hashmap_deinit(&orig); // not neccessary, but should still work without crashing
|
||||
}
|
||||
|
||||
TEST(str2str_copying, t) {
|
||||
struct apfl_hashmap orig = init_str2str(t);
|
||||
|
||||
check_set_str2str(t, &orig, "foo", "abc");
|
||||
check_set_str2str(t, &orig, "bar", "def");
|
||||
check_set_str2str(t, &orig, "baz", "ghi");
|
||||
check_set_str2str(t, &orig, "quux", "jkl");
|
||||
|
||||
struct apfl_hashmap copy;
|
||||
if (!apfl_hashmap_copy(©, orig)) {
|
||||
test_fatalf(t, "Copying failed");
|
||||
}
|
||||
|
||||
assert_len(t, orig, 4, "orig after moving");
|
||||
check_get_str2str(t, &orig, "foo", "abc");
|
||||
check_get_str2str(t, &orig, "bar", "def");
|
||||
check_get_str2str(t, &orig, "baz", "ghi");
|
||||
check_get_str2str(t, &orig, "quux", "jkl");
|
||||
|
||||
assert_len(t, copy, 4, "copy after moving");
|
||||
check_get_str2str(t, ©, "foo", "abc");
|
||||
check_get_str2str(t, ©, "bar", "def");
|
||||
check_get_str2str(t, ©, "baz", "ghi");
|
||||
check_get_str2str(t, ©, "quux", "jkl");
|
||||
|
||||
// Setting something in orig should not influence the copy
|
||||
check_set_str2str(t, &orig, "X", "xxx");
|
||||
check_not_exists_str2str(t, ©, "X");
|
||||
|
||||
// And the other way around too
|
||||
check_set_str2str(t, ©, "Y", "yyy");
|
||||
check_not_exists_str2str(t, &orig, "Y");
|
||||
|
||||
// Overwriting something in orig should not influence the copy
|
||||
check_set_str2str(t, &orig, "foo", "newfoo");
|
||||
check_get_str2str(t, ©, "foo", "abc");
|
||||
|
||||
// And the other way around too
|
||||
check_set_str2str(t, ©, "bar", "newbar");
|
||||
check_get_str2str(t, &orig, "bar", "def");
|
||||
|
||||
const char *k;
|
||||
// Deleting something in orig should not influence the copy
|
||||
k = "baz";
|
||||
apfl_hashmap_delete(&orig, &k);
|
||||
check_get_str2str(t, ©, k, "ghi");
|
||||
|
||||
// And the other way around too
|
||||
k = "quux";
|
||||
apfl_hashmap_delete(©, &k);
|
||||
check_get_str2str(t, &orig, k, "jkl");
|
||||
|
||||
apfl_hashmap_deinit(©);
|
||||
apfl_hashmap_deinit(&orig);
|
||||
}
|
||||
|
||||
TESTS_BEGIN
|
||||
ADDTEST(int2int_set_get_delete),
|
||||
ADDTEST(int2int_set_overwrite),
|
||||
ADDTEST(int2int_set_many),
|
||||
ADDTEST(int2int_iterating),
|
||||
ADDTEST(int2int_moving),
|
||||
ADDTEST(int2int_copying),
|
||||
|
||||
ADDTEST(str2str_set_get_delete),
|
||||
ADDTEST(str2str_set_overwrite),
|
||||
ADDTEST(str2str_set_many),
|
||||
ADDTEST(str2str_iterating),
|
||||
ADDTEST(str2str_moving),
|
||||
ADDTEST(str2str_copying),
|
||||
TESTS_END
|
||||
Loading…
Reference in a new issue